| name | Plugin Sync |
| description | Automatically generate plugin.yaml from Betty Framework registries |
plugin.sync
Overview
plugin.sync is the synchronization tool that generates plugin.yaml from Betty Framework's registry files. It ensures that Claude Code's plugin configuration stays in sync with registered skills, commands, and hooks.
Purpose
Automates the generation of plugin.yaml to maintain consistency between:
- Skill Registry (
registry/skills.json) – Active skills with entrypoints - Command Registry (
registry/commands.json) – Slash commands - Hook Registry (
registry/hooks.json) – Event-driven hooks - Plugin Configuration (
plugin.yaml) – Claude Code plugin manifest
This eliminates manual editing of plugin.yaml and prevents drift between what's registered and what's exposed to Claude Code.
What It Does
- Reads Registries: Loads
skills.json,commands.json, andhooks.json - Filters Active Skills: Processes only skills with
status: activeand defined entrypoints - Validates Handlers: Checks if handler files exist on disk
- Generates Commands: Converts skill entrypoints to
plugin.yamlcommand format - Preserves Metadata: Maintains existing plugin metadata (author, license, etc.)
- Writes plugin.yaml: Outputs formatted plugin configuration to repo root
- Reports Issues: Warns about missing handlers or inconsistencies
Usage
Basic Usage
python skills/plugin.sync/plugin_sync.py
No arguments required - reads from standard registry locations.
Via Betty CLI
/plugin/sync
Expected Registry Structure
The skill expects these registry files:
betty/
├── registry/
│ ├── skills.json # Registered skills
│ ├── commands.json # Registered commands
│ └── hooks.json # Registered hooks
└── plugin.yaml # Generated output
Behavior
1. Registry Loading
Reads JSON files from:
registry/skills.jsonregistry/commands.jsonregistry/hooks.json
If a registry file is missing, logs a warning and continues with available data.
2. Skill Processing
For each skill in skills.json:
- Checks status: Only processes skills with
status: active - Looks for entrypoints: Requires at least one entrypoint definition
- Validates handler: Checks if handler file exists at
skills/{skill_name}/{handler} - Converts format: Maps skill entrypoint to plugin command schema
3. Command Generation
Creates a command entry for each active skill entrypoint:
- name: skill/validate
description: Validate a skill manifest
handler:
runtime: python
script: skills/skill.define/skill_define.py
parameters:
- name: manifest_path
type: string
required: true
description: Path to skill.yaml file
permissions:
- filesystem:read
- filesystem:write
4. Handler Validation
For each handler reference:
- Constructs full path:
skills/{skill_name}/{handler_filename} - Checks file existence on disk
- Logs warning if handler file is missing
5. Plugin Generation
Preserves existing plugin.yaml metadata:
- Plugin name, version, description
- Author information
- License
- Requirements
- Permissions
- Config sections
Replaces the commands section with generated entries.
6. Output Writing
Writes plugin.yaml with:
- Auto-generated header comment
- Properly formatted YAML (2-space indent)
- Generation timestamp in metadata
- Skill and command counts
Outputs
Success Response
{
"ok": true,
"status": "success",
"output_path": "/home/user/betty/plugin.yaml",
"commands_generated": 18,
"warnings": []
}
Response with Warnings
{
"ok": true,
"status": "success",
"output_path": "/home/user/betty/plugin.yaml",
"commands_generated": 18,
"warnings": [
"Handler not found for 'api.validate': skills/api.validate/api_validate_missing.py",
"Skill 'test.broken' has entrypoint without handler"
]
}
Failure Response
{
"ok": false,
"status": "failed",
"error": "Failed to parse JSON from registry/skills.json: Expecting value: line 1 column 1"
}
Generated plugin.yaml Structure
The skill generates a plugin.yaml like this:
# Betty Framework - Claude Code Plugin
# Auto-generated by plugin.sync skill
# DO NOT EDIT MANUALLY - Run plugin.sync to regenerate
name: betty-framework
version: 1.0.0
description: Betty Framework - Structured AI-assisted engineering
author:
name: RiskExec
email: platform@riskexec.com
url: https://github.com/epieczko/betty
license: MIT
metadata:
homepage: https://github.com/epieczko/betty
repository: https://github.com/epieczko/betty
generated_at: "2025-10-23T17:45:00.123456+00:00"
generated_by: plugin.sync skill
skill_count: 18
command_count: 18
requirements:
python: ">=3.11"
packages:
- pyyaml
permissions:
- filesystem:read
- filesystem:write
- process:execute
commands:
- name: workflow/validate
description: Validates Betty workflow YAML definitions
handler:
runtime: python
script: skills/workflow.validate/workflow_validate.py
parameters:
- name: workflow.yaml
type: string
required: true
description: Path to the workflow YAML file
permissions:
- filesystem
- read
- name: skill/define
description: Validate a Claude Code skill manifest
handler:
runtime: python
script: skills/skill.define/skill_define.py
parameters:
- name: manifest_path
type: string
required: true
description: Path to the skill.yaml file
permissions:
- filesystem
- read
- write
# ... more commands ...
Validation and Warnings
Handler Existence Check
For each skill entrypoint, the skill checks:
full_path = f"skills/{skill_name}/{handler_filename}"
if not os.path.exists(full_path):
warnings.append(f"Handler not found: {full_path}")
Common Warnings
| Warning | Meaning | Action |
|---|---|---|
Handler not found for 'X' |
Handler file missing from disk | Create the handler or fix the path in skill.yaml |
Skill 'X' has entrypoint without handler |
Entrypoint missing handler field |
Add handler field to entrypoint definition |
Registry file not found |
Registry JSON is missing | Run registry update or check file paths |
Examples
Example 1: Full Sync After Adding New Skills
Scenario: You've added several new skills and want to sync them to plugin.yaml
# Create skills using skill.create
/skill/create data.transform "Transform data between formats"
/skill/create api.monitor "Monitor API health" --inputs=endpoint --outputs=status
# Define skills (validates and registers)
/skill/define skills/data.transform/skill.yaml
/skill/define skills/api.monitor/skill.yaml
# Sync to plugin.yaml
/plugin/sync
Output:
INFO: Starting plugin.yaml generation from registries...
INFO: Loading registry files...
INFO: Generating plugin.yaml configuration...
INFO: Added command: /data/transform from skill data.transform
INFO: Added command: /api/monitor from skill api.monitor
INFO: ✅ Written plugin.yaml to /home/user/betty/plugin.yaml
INFO: ✅ Generated 20 commands
INFO: 📄 Output: /home/user/betty/plugin.yaml
Example 2: Detecting Missing Handlers
Scenario: A skill's handler file was moved or deleted
# Remove a handler file
rm skills/api.validate/api_validate.py
# Run sync
/plugin/sync
Output:
INFO: Starting plugin.yaml generation from registries...
INFO: Loading registry files...
INFO: Generating plugin.yaml configuration...
INFO: Added command: /skill/define from skill skill.define
WARNING: Handler not found for 'api.validate': skills/api.validate/api_validate.py
INFO: ✅ Written plugin.yaml to /home/user/betty/plugin.yaml
INFO: ⚠️ Warnings during generation:
INFO: - Handler not found for 'api.validate': skills/api.validate/api_validate.py
INFO: ✅ Generated 19 commands
Example 3: Initial Plugin Setup
Scenario: Setting up Betty Framework plugin for the first time
# Initialize registry if needed
python -c "from betty.config import ensure_directories; ensure_directories()"
# Register all existing skills
for skill in skills/*/skill.yaml; do
/skill/define "$skill"
done
# Generate plugin.yaml
/plugin/sync
This creates a complete plugin.yaml from all registered active skills.
Integration
With skill.define
After defining a skill, sync the plugin:
/skill/define skills/my.skill/skill.yaml
/plugin/sync
With Workflows
Include plugin sync as a workflow step:
# workflows/skill_lifecycle.yaml
steps:
- skill: skill.create
args: ["new.skill", "Description"]
- skill: skill.define
args: ["skills/new.skill/skill.yaml"]
- skill: plugin.sync
args: []
With Hooks
Auto-sync when skills are updated:
# .claude/hooks.yaml
- event: on_file_save
pattern: "skills/*/skill.yaml"
command: python skills/skill.define/skill_define.py {file_path} && python skills/plugin.sync/plugin_sync.py
blocking: false
description: Auto-sync plugin.yaml when skills change
What Gets Included
✅ Included in plugin.yaml
- Skills with
status: active - Skills with at least one
entrypointdefined - All entrypoint parameters and permissions
- Skill descriptions and metadata
❌ Not Included
- Skills with
status: draft - Skills without entrypoints
- Skills marked as internal-only
- Test skills (unless marked active)
Common Errors
| Error | Cause | Solution |
|---|---|---|
| "Failed to parse JSON" | Invalid JSON in registry file | Fix JSON syntax in the registry |
| "Registry file not found" | Missing registry file | Ensure registries exist in registry/ dir |
| "Permission denied" | Cannot write plugin.yaml | Check file permissions on plugin.yaml |
| All commands missing | No active skills | Mark skills as active in registry |
Files Read
registry/skills.json– Skill registryregistry/commands.json– Command registry (future use)registry/hooks.json– Hook registry (future use)plugin.yaml– Existing plugin config (for metadata preservation)skills/*/– Handler file validation
Files Modified
plugin.yaml– Overwritten with generated configuration
Exit Codes
- 0: Success (plugin.yaml generated successfully)
- 1: Failure (error during generation)
Logging
Logs generation progress:
INFO: Starting plugin.yaml generation from registries...
INFO: Loading registry files...
INFO: Loaded existing plugin.yaml as template
INFO: Generating plugin.yaml configuration...
INFO: Added command: /skill/create from skill skill.create
INFO: Added command: /skill/define from skill skill.define
INFO: Added command: /agent/define from skill agent.define
INFO: ✅ Written plugin.yaml to /home/user/betty/plugin.yaml
INFO: ✅ Generated 18 commands
INFO: 📄 Output: /home/user/betty/plugin.yaml
Best Practices
- Run After Registry Changes: Sync after adding, updating, or removing skills
- Include in CI/CD: Add plugin sync to your deployment pipeline
- Review Before Commit: Check generated plugin.yaml before committing
- Keep Registries Clean: Remove inactive skills to keep plugin.yaml focused
- Use Hooks: Set up auto-sync hooks for convenience
- Version Control: Always commit plugin.yaml changes with skill changes
Troubleshooting
Plugin.yaml Not Updating
Problem: Changes to skills.json don't appear in plugin.yaml
Solutions:
- Ensure skill status is
active - Check that skill has
entrypointsdefined - Verify entrypoint has
commandandhandlerfields - Run
/skill/definebefore/plugin/sync
Handler Not Found Warnings
Problem: Warnings about missing handler files
Solutions:
- Check handler path in skill.yaml
- Ensure handler file exists at
skills/{skill_name}/{handler_filename} - Verify file permissions
- Update skill.yaml if handler was renamed
Commands Not Appearing
Problem: Active skill not generating command
Solutions:
- Verify skill has
entrypointsarray in skill.yaml - Check entrypoint has
commandfield (e.g.,/skill/validate) - Ensure
handlerfield points to correct file - Check that skill.yaml is in skills registry
Architecture
Skill Categories
Infrastructure – Plugin.sync maintains the foundation layer by syncing registry state to plugin configuration.
Design Principles
- Single Source of Truth: Registry files are the source of truth
- Idempotent: Can be run multiple times safely
- Validation First: Checks handlers before generating config
- Preserve Metadata: Keeps existing plugin metadata intact
- Clear Reporting: Detailed warnings about issues
See Also
- skill.define – Validate and register skills (SKILL.md)
- registry.update – Update skill registry (SKILL.md)
- skill.create – Create new skills (SKILL.md)
- Betty Architecture – Framework overview (betty-architecture.md)
Dependencies
- registry.update: Registry management
- betty.config: Configuration constants and paths
- betty.logging_utils: Logging infrastructure
Status
Active – Production-ready infrastructure skill
Version History
- 0.1.0 (Oct 2025) – Initial implementation with registry sync and handler validation