feat: Add git-based shared skill registry for team skill management

Adds skill_registry.py CLI tool with 7 subcommands (init, publish, list,
search, install, info, remove) for managing a git-friendly shared skill
catalog. No new dependencies — stdlib only. Integrates with existing
validate.py and security_scan.py for publish-time checks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
francylisboacharuto 2026-02-26 15:52:19 -03:00
parent bac2b27bb8
commit 736bbda78a
3 changed files with 1090 additions and 277 deletions

653
README.md
View file

@ -1,154 +1,203 @@
# Agent Skill Creator v4.0
# Agent Skill Creator
**Create Cross-Platform Agent Skills from Workflow Descriptions**
**Create cross-platform agent skills from natural language workflow descriptions.**
[![Agent Skills Open Standard](https://img.shields.io/badge/Agent%20Skills-Open%20Standard-blue)](https://github.com/anthropics/agent-skills-spec)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Version](https://img.shields.io/badge/version-4.0.0-brightgreen)]()
> Works on **8+ platforms**: Claude Code, GitHub Copilot, Cursor, Windsurf, Cline, Codex CLI, Gemini CLI, and any platform supporting the Agent Skills Open Standard.
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)]()
---
## What It Does
## What Is This?
Agent Skill Creator is a **meta-skill** -- a skill that creates other skills. Describe a repetitive workflow in natural language, and it generates a complete, validated, cross-platform agent skill through an autonomous 5-phase pipeline.
**Input**: A workflow description like *"Every day I download stock data, analyze trends, and create reports"*
Agent Skill Creator is a **meta-skill** -- a skill that creates other skills. Describe a repetitive workflow in plain English and it generates a complete, validated, cross-platform agent skill through an autonomous 5-phase pipeline.
**Input**: *"Every day I download stock data, analyze trends, and create reports"*
**Output**: A ready-to-install skill directory with functional scripts, documentation, cross-platform installer, and spec-compliant SKILL.md.
---
## Quick Start
### Install
### Claude Code
```bash
# Clone and install as a Claude Code skill
git clone https://github.com/user/agent-skill-creator.git
cp -r agent-skill-creator/ ~/.claude/skills/agent-skill-creator/
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git ~/.claude/skills/agent-skill-creator
```
### Use
### GitHub Copilot
Just describe what you need in your agent:
```
"Create a skill for analyzing stock market data"
"Every day I process CSV files manually, automate this"
"Create a cross-platform skill for weather alerts"
"Validate this skill for spec compliance"
"Export this skill for Cursor and Copilot"
```bash
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .github/skills/agent-skill-creator
```
The skill creator activates automatically when it detects these patterns and walks through the full pipeline.
### Cursor
```bash
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .cursor/rules/agent-skill-creator
```
After installing, open your agent and type:
```
Create a skill for analyzing CSV files
```
The skill creator activates and walks you through the full pipeline.
For Windsurf, Cline, Codex CLI, Gemini CLI, and other platforms see [Setup by Platform](#setup-by-platform-complete-guide) below.
---
## Supported Platforms
## Usage
Generated skills work on any platform that supports the Agent Skills Open Standard:
### Trigger Phrases
| Platform | Install Location | Notes |
|----------|-----------------|-------|
| **Claude Code** | `~/.claude/skills/` or `.claude/skills/` | Global or per-project |
| **GitHub Copilot** | `.github/skills/` | Repository-level |
| **Cursor** | `.cursor/rules/` | Workspace rules |
| **Windsurf** | `.windsurf/skills/` | Workspace skills |
| **Cline** | `.clinerules/` | Rule-based skills |
| **Codex CLI** | `.codex/skills/` | OpenAI Codex CLI |
| **Gemini CLI** | `.gemini/skills/` | Google Gemini CLI |
Say any of these to your agent:
Each generated skill includes an `install.sh` script that auto-detects your platform and installs to the correct location.
```
"Create a skill for analyzing stock market data"
"Every day I process CSV files manually, automate this"
"Create a cross-platform skill for weather alerts"
"Automate this workflow"
"I need to automate [repetitive task]"
"Validate this skill"
"Export this skill for Cursor and Copilot"
"Migrate this skill to v4"
```
### What Happens
The creator runs a **5-phase autonomous pipeline**:
```
Phase 1: DISCOVERY Research APIs, data sources, and domain knowledge
|
Phase 2: DESIGN Define use cases, methodologies, and outputs
|
Phase 3: ARCHITECTURE Structure skill directory (simple vs. complex suite)
|
Phase 4: DETECTION Generate description + keywords for reliable activation
|
Phase 5: IMPLEMENTATION Create all files, run validation, run security scan
```
Output: a complete skill directory you can install on any supported platform.
---
## Setup by Platform (Complete Guide)
Each platform installs with a single `git clone` directly into the right location. Replace `agent-skill-creator` with the skill name when installing generated skills.
### Claude Code
```bash
# Personal skill (available in all projects)
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git ~/.claude/skills/agent-skill-creator
# Per-project (scoped to one repo)
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .claude/skills/agent-skill-creator
```
### GitHub Copilot (CLI + VS Code)
```bash
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .github/skills/agent-skill-creator
```
### Cursor
```bash
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .cursor/rules/agent-skill-creator
```
Cursor reads SKILL.md natively alongside its `.mdc` rules.
### Windsurf
```bash
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .windsurf/skills/agent-skill-creator
```
### Cline
```bash
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .clinerules/agent-skill-creator
```
### OpenAI Codex CLI
```bash
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .codex/skills/agent-skill-creator
```
### Gemini CLI
```bash
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .gemini/skills/agent-skill-creator
```
### Claude Desktop / claude.ai (Export)
These platforms use `.zip` upload instead of directory copying:
1. Export: `python3 scripts/export_utils.py ./agent-skill-creator/ --variant desktop`
2. Open Claude Desktop or claude.ai
3. Go to Settings > Skills > Upload skill
4. Select the generated `.zip` file
### Claude API (Programmatic)
```bash
python3 scripts/export_utils.py ./agent-skill-creator/ --variant api
```
```python
import anthropic
client = anthropic.Anthropic()
with open("agent-skill-creator-api-v4.0.0.zip", "rb") as f:
skill = client.skills.create(file=f, name="agent-skill-creator")
response = client.messages.create(
model="claude-sonnet-4",
messages=[{"role": "user", "content": "Your query here"}],
container={"type": "custom_skill", "skill_id": skill.id},
betas=["code-execution-2025-08-25", "skills-2025-10-02"],
)
```
Note: API sandbox has no network access, no pip install at runtime, and an 8 MB size limit.
### Updating
To update an installed skill, just `git pull` from inside the skill directory:
```bash
cd ~/.claude/skills/agent-skill-creator && git pull
```
---
## How It Works
The creator runs a **5-phase autonomous pipeline**:
```
Phase 1: DISCOVERY Research APIs, data sources, tools, and domain knowledge
|
Phase 2: DESIGN Define use cases, analyses, methodologies, and outputs
|
Phase 3: ARCHITECTURE Structure skill directory (simple skill vs. complex suite)
|
Phase 4: DETECTION Generate description + keywords for reliable activation
|
Phase 5: IMPLEMENTATION Create all files, run validation, run security scan
```
### Phase Details
| Phase | What Happens | Key Output |
|-------|-------------|------------|
| **Discovery** | Researches the domain, identifies APIs and data sources, maps user needs | Domain model, API list, data sources |
| **Discovery** | Researches the domain, identifies APIs and data sources | Domain model, API list |
| **Design** | Defines use cases, analysis methods, output formats | Use case specs, methodology docs |
| **Architecture** | Decides simple skill vs. complex suite, plans directory structure | Architecture decision, file plan |
| **Detection** | Crafts SKILL.md description and activation keywords for reliable triggering | SKILL.md frontmatter, trigger phrases |
| **Implementation** | Generates all code, docs, installer; validates and scans for security issues | Complete skill directory |
| **Detection** | Crafts SKILL.md description and activation keywords | SKILL.md frontmatter, trigger phrases |
| **Implementation** | Generates all code, docs, installer; validates and scans | Complete skill directory |
For full pipeline documentation, see [references/pipeline-phases.md](references/pipeline-phases.md).
---
## Architecture: Simple Skill vs. Complex Suite
The creator automatically decides the right architecture based on scope:
### Simple Skill
For focused, single-domain tasks (e.g., "analyze CSV files", "extract text from PDFs").
```
stock-analyzer/
SKILL.md # Under 500 lines, spec-compliant
scripts/
analyze.py
fetch_data.py
references/
api-guide.md
assets/
report-template.html
install.sh
README.md
```
### Complex Suite
For multi-domain workflows requiring coordinated agents (e.g., "full financial analysis pipeline with data collection, analysis, and reporting").
```
financial-analysis-suite/
SKILL.md # Suite orchestrator, under 500 lines
scripts/
orchestrator.py
data_collector.py
analyzer.py
report_generator.py
references/
architecture-guide.md
api-reference.md
assets/
templates/
schemas/
install.sh
README.md
```
For detailed architecture guidance, see [references/architecture-guide.md](references/architecture-guide.md).
---
## Generated Skill Format
Every generated skill follows the Agent Skills Open Standard structure:
Every generated skill follows the Agent Skills Open Standard:
```
skill-name/
@ -160,9 +209,7 @@ skill-name/
README.md # Multi-platform install instructions
```
### SKILL.md Structure
The generated SKILL.md includes standard frontmatter:
### SKILL.md Frontmatter
```yaml
---
@ -182,45 +229,26 @@ compatibility: >-
Followed by sections: When to Use, Overview, Workflow, Implementation Guidelines, and References.
---
## Naming Convention
Skills follow the **Agent Skills Open Standard** naming rules:
- **Format**: `kebab-case` (lowercase letters and hyphens only)
- **Length**: 1-64 characters
- **Pattern**: `^[a-z][a-z0-9-]*[a-z0-9]$`
- **No special suffixes** required
### Examples
| Good | Bad |
|------|-----|
| `stock-analyzer` | `Stock_Analyzer` |
| `csv-data-cleaner` | `csv_data_cleaner` |
| `financial-analysis-suite` | `FinancialAnalysis` |
| `weather-alerts` | `weather-alerts-cskill` |
**Naming rules**: `kebab-case`, 1-64 characters, pattern `^[a-z][a-z0-9-]*[a-z0-9]$`, must match directory name.
---
## Validation and Security
## Tools
### Validate a Skill
Check that a generated skill is compliant with the Agent Skills Open Standard:
Check spec compliance against the Agent Skills Open Standard:
```bash
python3 scripts/validate.py ./my-skill/
# JSON output (for CI/CD)
python3 scripts/validate.py ./my-skill/ --json
```
Validates:
- SKILL.md exists and has valid frontmatter
- Name follows kebab-case convention (1-64 chars)
- Description is under 1024 characters
- SKILL.md is under 500 lines
- Required directory structure is present
- install.sh exists and is executable
**Checks**: SKILL.md existence, valid frontmatter, kebab-case name (1-64 chars), description under 1024 chars, body under 500 lines, required directory structure, install.sh exists and is executable.
**Exit codes**: `0` = valid (may have warnings), `1` = invalid (errors found).
### Security Scan
@ -228,165 +256,209 @@ Scan for common security issues before sharing or deploying:
```bash
python3 scripts/security_scan.py ./my-skill/
# JSON output
python3 scripts/security_scan.py ./my-skill/ --json
```
Detects:
- Hardcoded API keys, tokens, and secrets
- Potential command injection patterns
- Unsafe file operations
- Credential exposure in configuration files
**Detects**: hardcoded API keys (OpenAI, AWS, GitHub, GitLab), tokens and secrets, command injection patterns, unsafe file operations, credential exposure in config files.
---
**Exit codes**: `0` = clean, `1` = issues found.
## Cross-Platform Export
### Export for Other Platforms
Export skills for different deployment targets:
### Desktop/Web Export
Generates a `.zip` archive suitable for sharing or manual installation:
Package skills for distribution:
```bash
# Desktop/Web (.zip for Claude Desktop, claude.ai)
python3 scripts/export_utils.py ./my-skill/ --variant desktop
# API (.zip for Claude API, <=8MB)
python3 scripts/export_utils.py ./my-skill/ --variant api
# All variants
python3 scripts/export_utils.py ./my-skill/
```
### API Export
Output goes to `exports/`. See [references/export-guide.md](references/export-guide.md) for full documentation.
Generates a package suitable for Claude API integration:
### Skill Registry
Manage a shared skill catalog for teams using a git-based registry:
```bash
python3 scripts/export_utils.py ./my-skill/ --variant api
# Initialize a registry
python3 scripts/skill_registry.py init --registry ./my-registry --name "Team Skills"
# Publish a skill (validates and security-scans first)
python3 scripts/skill_registry.py publish ./my-skill/ --registry ./my-registry --tags data,csv
# List all published skills
python3 scripts/skill_registry.py list --registry ./my-registry
# Search for skills
python3 scripts/skill_registry.py search "finance" --registry ./my-registry
# Show full details about a skill
python3 scripts/skill_registry.py info stock-analyzer --registry ./my-registry
# Install a skill for a specific platform
python3 scripts/skill_registry.py install stock-analyzer --registry ./my-registry --platform claude-code
# Install at project level instead of user level
python3 scripts/skill_registry.py install stock-analyzer --registry ./my-registry --project
# Remove a skill from the registry
python3 scripts/skill_registry.py remove stock-analyzer --registry ./my-registry --force
```
For full export documentation, see [references/export-guide.md](references/export-guide.md).
All commands support `--json` for machine-readable output. The registry is a plain directory with `registry.json` and `skills/` — commit it to git for version history, access control via repo permissions, and review workflow via PRs.
**Exit codes**: `0` = success, `1` = error.
---
## Example Skill
## Architecture Decisions
The repository includes a complete example skill:
The creator automatically decides simple vs. complex based on scope:
### article-to-prototype
| Factor | Simple Skill | Complex Suite |
|--------|-------------|---------------|
| Workflows | 1-2 | 3+ distinct |
| Code size | <1000 lines | >2000 lines |
| Structure | Single SKILL.md | Multiple component SKILL.md files |
Converts academic articles and research papers into functional prototypes. Demonstrates the full skill structure including scripts, references, and cross-platform installer.
```
article-to-prototype/
SKILL.md
scripts/
article_processor.py
prototype_generator.py
validation_engine.py
references/
methodology.md
supported-formats.md
assets/
prototype-templates/
install.sh
README.md
```
See [article-to-prototype/](article-to-prototype/) for the full example.
For detailed decision logic, see [references/architecture-guide.md](references/architecture-guide.md).
---
## Project Structure
## For AI Agents (Machine-Readable Reference)
The agent-skill-creator repository itself:
This section provides structured metadata for AI agents ingesting this README as context.
### Activation Triggers
```
agent-skill-creator/
SKILL.md # Meta-skill definition (this skill's spec)
README.md # This file
MIGRATION.md # v3.x to v4.0 migration guide
.gitignore
scripts/
validate.py # Spec compliance validator
security_scan.py # Security scanner
export_utils.py # Cross-platform export tool
install-template.sh # Template for generated install.sh
references/
pipeline-phases.md # Full 5-phase pipeline docs
architecture-guide.md # Simple skill vs. complex suite
cross-platform-guide.md # Platform-specific details
export-guide.md # Export system documentation
phase1-discovery.md # Phase 1 deep dive
phase2-design.md # Phase 2 deep dive
phase3-architecture.md # Phase 3 deep dive
phase4-detection.md # Phase 4 deep dive
phase5-implementation.md # Phase 5 deep dive
phase6-testing.md # Testing guide
quality-standards.md # Quality standards reference
templates-guide.md # Template system guide
templates/ # Skill templates
tools/ # Validation and scanning tools
examples/ # Example configurations
integrations/
agentdb_bridge.py # AgentDB integration bridge
agentdb_real_integration.py
fallback_system.py # Graceful degradation system
learning_feedback.py # Learning loop integration
validation_system.py # Integration validation
article-to-prototype/ # Example generated skill
exports/ # Export output directory
docs/ # Additional documentation
CHANGELOG.md # Version history
NAMING_CONVENTIONS.md # Naming rules reference
PIPELINE_ARCHITECTURE.md # Pipeline internals
DECISION_LOGIC.md # Architecture decision logic
CLAUDE_SKILLS_ARCHITECTURE.md
create an agent for [objective]
create a skill for [domain]
develop a custom skill
automate this workflow
every day I [task]
I repeatedly need to [process]
I need to automate [task]
create a cross-platform skill for [objective]
validate this skill
check if this skill is spec-compliant
export this skill for [platform]
package this skill for [platform]
migrate this skill to v4
update this skill to the new standard
create a multi-agent suite for [objective]
create a skill from the [name] template
```
### Install Commands
```bash
# Claude Code (personal)
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git ~/.claude/skills/agent-skill-creator
# GitHub Copilot
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .github/skills/agent-skill-creator
# Cursor
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .cursor/rules/agent-skill-creator
# Windsurf
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .windsurf/skills/agent-skill-creator
# Cline
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .clinerules/agent-skill-creator
# Codex CLI
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .codex/skills/agent-skill-creator
# Gemini CLI
git clone https://github.com/FrancyJGLisboa/agent-skill-creator.git .gemini/skills/agent-skill-creator
# Update
cd <install-path>/agent-skill-creator && git pull
```
### Tool Commands
```bash
# Validate
python3 scripts/validate.py PATH # Human output
python3 scripts/validate.py PATH --json # Machine output
# Security scan
python3 scripts/security_scan.py PATH
python3 scripts/security_scan.py PATH --json
# Export
python3 scripts/export_utils.py PATH --variant desktop
python3 scripts/export_utils.py PATH --variant api
# Registry
python3 scripts/skill_registry.py init --registry PATH --name NAME
python3 scripts/skill_registry.py publish SKILL_PATH --registry PATH --tags T1,T2
python3 scripts/skill_registry.py list --registry PATH [--json]
python3 scripts/skill_registry.py search QUERY --registry PATH [--json]
python3 scripts/skill_registry.py install SKILL_NAME --registry PATH --platform PLATFORM
python3 scripts/skill_registry.py info SKILL_NAME --registry PATH [--json]
python3 scripts/skill_registry.py remove SKILL_NAME --registry PATH --force
```
### Platform Paths
| Platform | Path | Scope |
|----------|------|-------|
| Claude Code | `~/.claude/skills/` | User-level |
| Claude Code | `.claude/skills/` | Project-level |
| GitHub Copilot | `.github/skills/` | Project-level |
| Cursor | `.cursor/rules/` | Workspace |
| Windsurf | `.windsurf/skills/` | Workspace |
| Cline | `.clinerules/` | Workspace |
| Codex CLI | `.codex/skills/` | Workspace |
| Gemini CLI | `.gemini/skills/` | Workspace |
| Claude Desktop | `.zip` upload | App-level |
| claude.ai | `.zip` upload | Web |
| Claude API | `.zip` via API | Programmatic |
### SKILL.md Spec (Required Fields)
```yaml
---
## Activation Triggers
The skill creator activates when it detects phrases like:
- "Create an agent for ..."
- "Create a skill for ..."
- "Automate this workflow"
- "Every day I have to ..."
- "I need to automate ..."
- "Create a cross-platform skill for ..."
- "Validate this skill"
- "Export this skill for [platform]"
- "Migrate this skill to v4"
See [references/phase4-detection.md](references/phase4-detection.md) for the full activation pattern reference.
name: kebab-case-name # 1-64 chars, ^[a-z][a-z0-9-]*[a-z0-9]$
description: >- # 1-1024 chars, include activation keywords
What this skill does...
license: MIT
metadata:
author: Author Name
version: X.Y.Z
---
# Body: <500 lines. Move detailed content to references/.
```
## AgentDB Integration (Optional)
### Pipeline Phases
Skills can optionally integrate with AgentDB for persistent learning across sessions:
```
DISCOVERY -> DESIGN -> ARCHITECTURE -> DETECTION -> IMPLEMENTATION
```
- **Learning feedback**: Skills improve based on usage patterns
- **Cross-session memory**: Retain context between conversations
- **Performance metrics**: Track skill effectiveness over time
AgentDB is not required. Skills work fully without it. See [references/agentdb-integration.md](references/agentdb-integration.md) for setup details.
Each phase is documented in `references/phase{1..5}-*.md`.
---
## Migration from v3.x
If you have skills created with v3.x of agent-skill-creator:
Key changes in v4.0:
**Key changes in v4.0:**
- `-cskill` suffix removed from skill names (use standard kebab-case)
- `marketplace.json` simplified (optional for simple skills)
- SKILL.md body limited to 500 lines (move detail to `references/`)
- `install.sh` cross-platform installer added
- Spec validation and security scanning tools added
- `marketplace.json` simplified (optional for simple skills)
Quick migration:
**Quick migration:**
```bash
# Rename directory (remove -cskill suffix)
mv my-skill-cskill/ my-skill/
# Update SKILL.md name field
# Validate the migrated skill
# Update SKILL.md name field to remove -cskill suffix
python3 scripts/validate.py ./my-skill/
```
@ -394,38 +466,65 @@ For the complete migration guide, see [MIGRATION.md](MIGRATION.md).
---
## Advanced Features
## Troubleshooting
### Interactive Mode
**Skill not activating**: Ensure SKILL.md `description` field contains the trigger phrases you expect. The description is the primary activation mechanism.
For complex skills, the creator can run in interactive mode, asking clarifying questions before generating:
**Validation fails on name**: Names must be kebab-case, 1-64 characters, no consecutive hyphens, no leading/trailing hyphens. Pattern: `^[a-z][a-z0-9-]*[a-z0-9]$`.
**SKILL.md too long**: Body must be under 500 lines. Move detailed documentation to `references/` and link from the main SKILL.md.
**Export fails with size error**: API exports have an 8 MB limit. Reduce asset sizes or exclude large files.
**install.sh not executable**: Run `chmod +x install.sh` before executing.
**Platform not auto-detected**: Use `./install.sh --platform <name>` to specify explicitly.
---
## Project Structure
```
"Create a skill for financial analysis" (interactive)
agent-skill-creator/
SKILL.md # Meta-skill definition
README.md # This file
MIGRATION.md # v3.x to v4.0 migration guide
scripts/
validate.py # Spec compliance validator
security_scan.py # Security scanner
export_utils.py # Cross-platform export tool
skill_registry.py # Git-based shared skill registry
install-template.sh # Template for generated install.sh
references/
pipeline-phases.md # Full 5-phase pipeline docs
architecture-guide.md # Simple skill vs. complex suite
cross-platform-guide.md # Platform-specific details
export-guide.md # Export system documentation
phase1-discovery.md # Phase 1 deep dive
phase2-design.md # Phase 2 deep dive
phase3-architecture.md # Phase 3 deep dive
phase4-detection.md # Phase 4 deep dive
phase5-implementation.md # Phase 5 deep dive
phase6-testing.md # Testing guide
quality-standards.md # Quality standards reference
templates-guide.md # Template system guide
templates/ # Skill templates
tools/ # Validation and scanning tools
examples/ # Example configurations
integrations/
agentdb_bridge.py # AgentDB integration bridge
fallback_system.py # Graceful degradation system
learning_feedback.py # Learning loop integration
validation_system.py # Integration validation
article-to-prototype/ # Example generated skill
exports/ # Export output directory
docs/
CHANGELOG.md # Version history
NAMING_CONVENTIONS.md # Naming rules reference
PIPELINE_ARCHITECTURE.md # Pipeline internals
DECISION_LOGIC.md # Architecture decision logic
```
See [references/interactive-mode.md](references/interactive-mode.md).
### Multi-Agent Suites
Create coordinated multi-agent systems where specialized agents collaborate:
```
"Create a multi-agent suite for end-to-end data pipeline"
```
See [references/multi-agent-guide.md](references/multi-agent-guide.md).
### Template-Based Creation
Use pre-built templates to accelerate skill creation:
```
"Create a skill from the data-analysis template"
```
See [references/templates-guide.md](references/templates-guide.md).
---
## Contributing
@ -441,7 +540,7 @@ See [references/templates-guide.md](references/templates-guide.md).
## License
MIT License. See [LICENSE](LICENSE) for details.
MIT License.
---

686
scripts/skill_registry.py Normal file
View file

@ -0,0 +1,686 @@
#!/usr/bin/env python3
"""
Git-Based Shared Skill Registry.
Manages a git-friendly skill registry for publishing, discovering, and installing
cross-platform agent skills. The registry is a directory with a registry.json
manifest and a skills/ folder no servers, no databases, no new dependencies.
Usage:
python3 scripts/skill_registry.py init [--name NAME] [--registry PATH]
python3 scripts/skill_registry.py publish <skill-path> [--registry PATH] [--tags T1,T2] [--force] [--json]
python3 scripts/skill_registry.py list [--registry PATH] [--json]
python3 scripts/skill_registry.py search <query> [--registry PATH] [--json]
python3 scripts/skill_registry.py install <skill-name> [--registry PATH] [--platform PLATFORM] [--project] [--force] [--json]
python3 scripts/skill_registry.py info <skill-name> [--registry PATH] [--json]
python3 scripts/skill_registry.py remove <skill-name> [--registry PATH] [--force]
Exit codes:
0 - Success
1 - Error
"""
import argparse
import json
import os
import re
import shutil
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional
# --- Import sibling scripts ---
_SCRIPTS_DIR = Path(__file__).resolve().parent
if str(_SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPTS_DIR))
from validate import validate_skill, _parse_frontmatter, _parse_yaml_field, _parse_subfield_value
from security_scan import security_scan
# --- Constants ---
ALL_PLATFORMS = ["claude-code", "copilot", "cursor", "windsurf", "cline", "codex", "gemini"]
PLATFORM_PATHS_USER = {
"claude-code": "~/.claude/skills",
"copilot": "~/.copilot/skills",
"cursor": "~/.cursor/rules",
"windsurf": "~/.windsurf/skills",
"cline": "~/.cline/rules",
"codex": "~/.codex/skills",
"gemini": "~/.gemini/skills",
}
PLATFORM_PATHS_PROJECT = {
"claude-code": ".claude/skills",
"copilot": ".github/skills",
"cursor": ".cursor/rules",
"windsurf": ".windsurf/skills",
"cline": ".clinerules",
"codex": ".codex/skills",
"gemini": ".gemini/skills",
}
# Directories/files to exclude when copying skills
COPY_IGNORE_PATTERNS = shutil.ignore_patterns(
".git", "__pycache__", "node_modules", ".venv", "venv", "env",
".pytest_cache", ".mypy_cache", "dist", "build", "*.pyc", "*.pyo",
)
# Stop words for auto-tagging
STOP_WORDS = {
"a", "an", "the", "and", "or", "but", "is", "are", "was", "were", "be",
"been", "being", "in", "on", "at", "to", "for", "of", "with", "by",
"from", "as", "into", "through", "during", "before", "after", "above",
"below", "between", "out", "off", "over", "under", "again", "further",
"then", "once", "here", "there", "when", "where", "why", "how", "all",
"each", "every", "both", "few", "more", "most", "other", "some", "such",
"no", "nor", "not", "only", "own", "same", "so", "than", "too", "very",
"can", "will", "just", "should", "now", "it", "its", "this", "that",
"these", "those", "he", "she", "we", "they", "what", "which", "who",
"whom", "do", "does", "did", "has", "have", "had", "having", "using",
}
MIN_TAG_LENGTH = 3
# --- Registry I/O ---
def load_registry(registry_path: Path) -> dict:
"""Read and parse registry.json from the registry directory."""
manifest = registry_path / "registry.json"
if not manifest.exists():
print(f"Error: registry.json not found in {registry_path}", file=sys.stderr)
print("Run 'skill_registry.py init' first.", file=sys.stderr)
sys.exit(1)
try:
return json.loads(manifest.read_text(encoding="utf-8"))
except (json.JSONDecodeError, OSError) as exc:
print(f"Error reading registry.json: {exc}", file=sys.stderr)
sys.exit(1)
def save_registry(registry_path: Path, data: dict) -> None:
"""Atomic write: write to .tmp then rename."""
manifest = registry_path / "registry.json"
tmp = registry_path / "registry.json.tmp"
try:
tmp.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
tmp.replace(manifest)
except OSError as exc:
# Clean up tmp on failure
if tmp.exists():
tmp.unlink()
print(f"Error writing registry.json: {exc}", file=sys.stderr)
sys.exit(1)
# --- Metadata Extraction ---
def extract_skill_metadata(skill_path: Path) -> dict:
"""
Parse SKILL.md frontmatter into a metadata dict.
Returns dict with keys: name, description, version, author, license.
Missing fields default to empty string.
"""
skill_md = skill_path / "SKILL.md"
if not skill_md.exists():
return {"name": "", "description": "", "version": "", "author": "", "license": ""}
content = skill_md.read_text(encoding="utf-8")
frontmatter, _ = _parse_frontmatter(content)
if frontmatter is None:
return {"name": "", "description": "", "version": "", "author": "", "license": ""}
name = _parse_yaml_field(frontmatter, "name") or ""
description = _parse_yaml_field(frontmatter, "description") or ""
license_val = _parse_yaml_field(frontmatter, "license") or ""
# Version: try metadata.version first, then top-level version
version = _parse_subfield_value(frontmatter, "metadata", "version")
if not version:
version = _parse_yaml_field(frontmatter, "version") or ""
# Author: try metadata.author first
author = _parse_subfield_value(frontmatter, "metadata", "author") or ""
return {
"name": name.strip(),
"description": description.strip(),
"version": version.strip(),
"author": author.strip(),
"license": license_val.strip(),
}
def auto_extract_tags(description: str) -> list[str]:
"""
Extract keyword tags from a description string.
Splits on non-alphanumeric characters, filters stop words and short words,
returns up to 10 unique lowercase tags.
"""
if not description:
return []
words = re.split(r"[^a-zA-Z0-9-]+", description.lower())
seen: set[str] = set()
tags: list[str] = []
for word in words:
word = word.strip("-")
if len(word) < MIN_TAG_LENGTH:
continue
if word in STOP_WORDS:
continue
if word not in seen:
seen.add(word)
tags.append(word)
if len(tags) >= 10:
break
return tags
# --- Platform Detection ---
def detect_platform() -> str:
"""
Auto-detect the installed agent platform by checking known directories.
Returns the platform name or "claude-code" as default.
"""
checks = [
("claude-code", "~/.claude"),
("copilot", "~/.copilot"),
("cursor", "~/.cursor"),
("windsurf", "~/.windsurf"),
("cline", "~/.cline"),
("codex", "~/.codex"),
("gemini", "~/.gemini"),
]
for platform, path in checks:
if Path(path).expanduser().exists():
return platform
return "claude-code"
def resolve_install_path(name: str, platform: str, project: bool) -> Path:
"""
Map platform + scope to the filesystem install path for a skill.
Args:
name: Skill name (used as subdirectory).
platform: Platform identifier.
project: If True, use project-level path; otherwise user-level.
Returns:
Absolute path where the skill should be installed.
"""
if project:
base = PLATFORM_PATHS_PROJECT.get(platform)
else:
base = PLATFORM_PATHS_USER.get(platform)
if base is None:
print(f"Error: unknown platform '{platform}'", file=sys.stderr)
print(f"Supported: {', '.join(ALL_PLATFORMS)}", file=sys.stderr)
sys.exit(1)
return Path(base).expanduser().resolve() / name
# --- Table Formatting ---
def _format_table(entries: list[dict]) -> str:
"""Format skill entries as an aligned text table."""
if not entries:
return "No skills found."
headers = ["NAME", "VERSION", "AUTHOR", "TAGS"]
rows = []
for entry in entries:
tags = ", ".join(entry.get("tags", []))
rows.append([
entry.get("name", ""),
entry.get("version", ""),
entry.get("author", ""),
tags,
])
# Calculate column widths
widths = [len(h) for h in headers]
for row in rows:
for i, cell in enumerate(row):
widths[i] = max(widths[i], len(cell))
# Build output
lines = []
header_line = " ".join(h.ljust(widths[i]) for i, h in enumerate(headers))
lines.append(header_line)
for row in rows:
lines.append(" ".join(cell.ljust(widths[i]) for i, cell in enumerate(row)))
return "\n".join(lines)
# --- Subcommands ---
def cmd_init(args: argparse.Namespace) -> None:
"""Initialize a new skill registry."""
registry_path = Path(args.registry).resolve()
manifest = registry_path / "registry.json"
if manifest.exists():
print(f"Error: registry already exists at {registry_path}", file=sys.stderr)
sys.exit(1)
registry_path.mkdir(parents=True, exist_ok=True)
(registry_path / "skills").mkdir(exist_ok=True)
name = args.name or "Shared Skills"
data = {
"registry": {
"name": name,
"created": datetime.now(timezone.utc).isoformat(timespec="seconds"),
"schema_version": "1",
},
"skills": [],
}
save_registry(registry_path, data)
print(f"Registry initialized: {registry_path}")
print(f" Name: {name}")
print(f" Manifest: {manifest}")
print(f" Skills dir: {registry_path / 'skills'}")
def cmd_publish(args: argparse.Namespace) -> None:
"""Publish a skill to the registry."""
registry_path = Path(args.registry).resolve()
skill_path = Path(args.skill_path).resolve()
if not skill_path.is_dir():
print(f"Error: skill path is not a directory: {skill_path}", file=sys.stderr)
sys.exit(1)
# Step 1: Validate
validation = validate_skill(str(skill_path))
if not validation["valid"]:
print("Validation failed:", file=sys.stderr)
for err in validation["errors"]:
print(f" [ERROR] {err}", file=sys.stderr)
sys.exit(1)
# Step 2: Security scan
scan = security_scan(str(skill_path))
high_issues = [i for i in scan["issues"] if i["severity"] == "high"]
other_issues = [i for i in scan["issues"] if i["severity"] != "high"]
if other_issues:
for issue in other_issues:
location = issue["file"]
if issue["line"] > 0:
location += f":{issue['line']}"
print(f" [WARN] {location}: {issue['description']}")
if high_issues and not args.force:
print("Security scan found high-severity issues:", file=sys.stderr)
for issue in high_issues:
location = issue["file"]
if issue["line"] > 0:
location += f":{issue['line']}"
print(f" [HIGH] {location}: {issue['description']}", file=sys.stderr)
print("Use --force to publish anyway.", file=sys.stderr)
sys.exit(1)
# Step 3: Extract metadata
metadata = extract_skill_metadata(skill_path)
name = metadata["name"]
version = metadata["version"] or "0.0.0"
if not name:
print("Error: could not extract skill name from SKILL.md frontmatter", file=sys.stderr)
sys.exit(1)
# Step 4: Tags
tags = []
if args.tags:
tags = [t.strip() for t in args.tags.split(",") if t.strip()]
if not tags:
tags = auto_extract_tags(metadata["description"])
# Step 5: Check duplicates
data = load_registry(registry_path)
for existing in data["skills"]:
if existing["name"] == name and existing["version"] == version:
if not args.force:
print(
f"Error: skill '{name}' version '{version}' already exists in registry.",
file=sys.stderr,
)
print("Use --force to overwrite.", file=sys.stderr)
sys.exit(1)
# Remove old entry if forcing
data["skills"] = [s for s in data["skills"] if not (s["name"] == name and s["version"] == version)]
# Step 6: Copy skill to registry
dest = registry_path / "skills" / name
if dest.exists():
shutil.rmtree(dest)
shutil.copytree(skill_path, dest, ignore=COPY_IGNORE_PATTERNS)
# Step 7: Add entry
entry = {
"name": name,
"description": metadata["description"],
"version": version,
"author": metadata["author"],
"license": metadata["license"],
"tags": tags,
"platforms": list(ALL_PLATFORMS),
"published": datetime.now(timezone.utc).isoformat(timespec="seconds"),
"path": f"skills/{name}",
"validation": {
"valid": validation["valid"],
"errors": len(validation["errors"]),
"warnings": len(validation["warnings"]),
},
"security": {
"clean": scan["clean"],
"issues": len(scan["issues"]),
},
}
data["skills"].append(entry)
save_registry(registry_path, data)
if getattr(args, "json", False):
print(json.dumps(entry, indent=2))
else:
print(f"Published '{name}' v{version} to registry.")
print(f" Path: {dest}")
print(f" Tags: {', '.join(tags)}")
def cmd_list(args: argparse.Namespace) -> None:
"""List all skills in the registry."""
registry_path = Path(args.registry).resolve()
data = load_registry(registry_path)
if getattr(args, "json", False):
print(json.dumps(data["skills"], indent=2))
return
print(_format_table(data["skills"]))
def cmd_search(args: argparse.Namespace) -> None:
"""Search for skills matching a query."""
registry_path = Path(args.registry).resolve()
data = load_registry(registry_path)
query = args.query.lower()
matches = []
for skill in data["skills"]:
searchable = " ".join([
skill.get("name", ""),
skill.get("description", ""),
skill.get("author", ""),
" ".join(skill.get("tags", [])),
]).lower()
if query in searchable:
matches.append(skill)
if getattr(args, "json", False):
print(json.dumps(matches, indent=2))
return
if not matches:
print(f"No skills matching '{args.query}'.")
return
print(f"Skills matching '{args.query}':\n")
print(_format_table(matches))
def cmd_install(args: argparse.Namespace) -> None:
"""Install a skill from the registry."""
registry_path = Path(args.registry).resolve()
data = load_registry(registry_path)
# Find skill
skill_entry = None
for skill in data["skills"]:
if skill["name"] == args.skill_name:
skill_entry = skill
break
if skill_entry is None:
print(f"Error: skill '{args.skill_name}' not found in registry.", file=sys.stderr)
sys.exit(1)
# Resolve platform
platform = args.platform or detect_platform()
if platform not in ALL_PLATFORMS:
print(f"Error: unknown platform '{platform}'", file=sys.stderr)
print(f"Supported: {', '.join(ALL_PLATFORMS)}", file=sys.stderr)
sys.exit(1)
# Resolve target path
project = getattr(args, "project", False)
target = resolve_install_path(args.skill_name, platform, project)
# Check if already installed
if target.exists() and not args.force:
print(f"Error: skill already installed at {target}", file=sys.stderr)
print("Use --force to overwrite.", file=sys.stderr)
sys.exit(1)
# Copy
source = registry_path / skill_entry["path"]
if not source.exists():
print(f"Error: skill files not found at {source}", file=sys.stderr)
sys.exit(1)
if target.exists():
shutil.rmtree(target)
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copytree(source, target, ignore=COPY_IGNORE_PATTERNS)
if getattr(args, "json", False):
print(json.dumps({
"installed": True,
"skill": args.skill_name,
"platform": platform,
"path": str(target),
}, indent=2))
return
scope = "project" if project else "user"
print(f"Installed '{args.skill_name}' for {platform} ({scope}-level).")
print(f" Path: {target}")
# Platform-specific activation tips
tips = {
"claude-code": "Skill is auto-loaded. Start a new conversation to activate.",
"copilot": "Skill is auto-loaded by Copilot Chat.",
"cursor": "Skill is loaded alongside .mdc rules.",
"windsurf": "Skill is auto-loaded by Windsurf.",
"cline": "Skill is loaded from .clinerules.",
"codex": "Skill is auto-loaded by Codex CLI.",
"gemini": "Skill is auto-loaded by Gemini CLI.",
}
tip = tips.get(platform)
if tip:
print(f" Tip: {tip}")
def cmd_info(args: argparse.Namespace) -> None:
"""Show detailed info about a skill."""
registry_path = Path(args.registry).resolve()
data = load_registry(registry_path)
skill_entry = None
for skill in data["skills"]:
if skill["name"] == args.skill_name:
skill_entry = skill
break
if skill_entry is None:
print(f"Error: skill '{args.skill_name}' not found in registry.", file=sys.stderr)
sys.exit(1)
if getattr(args, "json", False):
print(json.dumps(skill_entry, indent=2))
return
print(f"Skill: {skill_entry['name']}")
print(f"{'=' * 50}")
print(f" Version: {skill_entry.get('version', 'N/A')}")
print(f" Author: {skill_entry.get('author', 'N/A')}")
print(f" License: {skill_entry.get('license', 'N/A')}")
print(f" Description: {skill_entry.get('description', 'N/A')}")
print(f" Tags: {', '.join(skill_entry.get('tags', []))}")
print(f" Platforms: {', '.join(skill_entry.get('platforms', []))}")
print(f" Published: {skill_entry.get('published', 'N/A')}")
print(f" Path: {skill_entry.get('path', 'N/A')}")
validation = skill_entry.get("validation", {})
if validation:
status = "valid" if validation.get("valid") else "invalid"
print(f" Validation: {status} ({validation.get('errors', 0)} errors, {validation.get('warnings', 0)} warnings)")
security = skill_entry.get("security", {})
if security:
status = "clean" if security.get("clean") else f"{security.get('issues', 0)} issues"
print(f" Security: {status}")
print(f"{'=' * 50}")
def cmd_remove(args: argparse.Namespace) -> None:
"""Remove a skill from the registry."""
registry_path = Path(args.registry).resolve()
data = load_registry(registry_path)
# Find skill
skill_entry = None
for skill in data["skills"]:
if skill["name"] == args.skill_name:
skill_entry = skill
break
if skill_entry is None:
print(f"Error: skill '{args.skill_name}' not found in registry.", file=sys.stderr)
sys.exit(1)
if not args.force:
print(f"Remove '{args.skill_name}' from registry? Use --force to confirm.", file=sys.stderr)
sys.exit(1)
# Remove files
skill_dir = registry_path / skill_entry["path"]
if skill_dir.exists():
shutil.rmtree(skill_dir)
# Remove entry
data["skills"] = [s for s in data["skills"] if s["name"] != args.skill_name]
save_registry(registry_path, data)
print(f"Removed '{args.skill_name}' from registry.")
# --- CLI ---
def _add_registry_arg(parser: argparse.ArgumentParser) -> None:
"""Add the --registry argument to a subparser."""
parser.add_argument(
"--registry", default="./registry",
help="Path to the registry directory (default: ./registry)",
)
def build_parser() -> argparse.ArgumentParser:
"""Build the argument parser with all subcommands."""
parser = argparse.ArgumentParser(
prog="skill_registry",
description="Git-based shared skill registry for cross-platform agent skills.",
)
subparsers = parser.add_subparsers(dest="command", help="Available commands")
# init
p_init = subparsers.add_parser("init", help="Initialize a new skill registry")
_add_registry_arg(p_init)
p_init.add_argument("--name", help="Registry name (default: 'Shared Skills')")
# publish
p_publish = subparsers.add_parser("publish", help="Publish a skill to the registry")
p_publish.add_argument("skill_path", help="Path to the skill directory")
_add_registry_arg(p_publish)
p_publish.add_argument("--tags", help="Comma-separated tags (auto-extracted if omitted)")
p_publish.add_argument("--force", action="store_true", help="Overwrite existing or ignore high-severity issues")
p_publish.add_argument("--json", action="store_true", help="Output as JSON")
# list
p_list = subparsers.add_parser("list", help="List all skills in the registry")
_add_registry_arg(p_list)
p_list.add_argument("--json", action="store_true", help="Output as JSON")
# search
p_search = subparsers.add_parser("search", help="Search for skills")
p_search.add_argument("query", help="Search query (matches name, description, author, tags)")
_add_registry_arg(p_search)
p_search.add_argument("--json", action="store_true", help="Output as JSON")
# install
p_install = subparsers.add_parser("install", help="Install a skill from the registry")
p_install.add_argument("skill_name", help="Name of the skill to install")
_add_registry_arg(p_install)
p_install.add_argument("--platform", choices=ALL_PLATFORMS, help="Target platform (auto-detected if omitted)")
p_install.add_argument("--project", action="store_true", help="Install at project level instead of user level")
p_install.add_argument("--force", action="store_true", help="Overwrite existing installation")
p_install.add_argument("--json", action="store_true", help="Output as JSON")
# info
p_info = subparsers.add_parser("info", help="Show detailed info about a skill")
p_info.add_argument("skill_name", help="Name of the skill")
_add_registry_arg(p_info)
p_info.add_argument("--json", action="store_true", help="Output as JSON")
# remove
p_remove = subparsers.add_parser("remove", help="Remove a skill from the registry")
p_remove.add_argument("skill_name", help="Name of the skill to remove")
_add_registry_arg(p_remove)
p_remove.add_argument("--force", action="store_true", help="Confirm removal")
return parser
def main() -> None:
"""CLI entry point."""
parser = build_parser()
args = parser.parse_args()
if args.command is None:
parser.print_help()
sys.exit(1)
commands = {
"init": cmd_init,
"publish": cmd_publish,
"list": cmd_list,
"search": cmd_search,
"install": cmd_install,
"info": cmd_info,
"remove": cmd_remove,
}
cmd_func = commands.get(args.command)
if cmd_func is None:
parser.print_help()
sys.exit(1)
cmd_func(args)
if __name__ == "__main__":
main()

View file

@ -147,6 +147,34 @@ def _subfield_exists(frontmatter: str, parent: str, child: str) -> bool:
return False
def _parse_subfield_value(frontmatter: str, parent: str, child: str) -> Optional[str]:
"""
Extract a sub-field value from under a parent field in YAML frontmatter.
Args:
frontmatter: The frontmatter text.
parent: The parent field name (e.g., ``metadata``).
child: The child field name (e.g., ``author``).
Returns:
The sub-field value as a string, or None if not found.
"""
lines = frontmatter.split("\n")
in_parent = False
for line in lines:
stripped = line.strip()
if stripped.startswith(f"{parent}:"):
in_parent = True
continue
if in_parent:
if line and (line[0] == " " or line[0] == "\t"):
if stripped.startswith(f"{child}:"):
return stripped[len(child) + 1:].strip()
else:
in_parent = False
return None
def _extract_local_links(body: str) -> list[str]:
"""
Extract local file paths referenced in markdown links within the body.