Claude Code Plugins

Community-maintained marketplace

Feedback

Plugin Sync

@epieczko/betty
0
0

Automatically generate plugin.yaml from Betty Framework registries

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

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

  1. Reads Registries: Loads skills.json, commands.json, and hooks.json
  2. Filters Active Skills: Processes only skills with status: active and defined entrypoints
  3. Validates Handlers: Checks if handler files exist on disk
  4. Generates Commands: Converts skill entrypoints to plugin.yaml command format
  5. Preserves Metadata: Maintains existing plugin metadata (author, license, etc.)
  6. Writes plugin.yaml: Outputs formatted plugin configuration to repo root
  7. 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.json
  • registry/commands.json
  • registry/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 entrypoint defined
  • 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 registry
  • registry/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

  1. Run After Registry Changes: Sync after adding, updating, or removing skills
  2. Include in CI/CD: Add plugin sync to your deployment pipeline
  3. Review Before Commit: Check generated plugin.yaml before committing
  4. Keep Registries Clean: Remove inactive skills to keep plugin.yaml focused
  5. Use Hooks: Set up auto-sync hooks for convenience
  6. 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 entrypoints defined
  • Verify entrypoint has command and handler fields
  • Run /skill/define before /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 entrypoints array in skill.yaml
  • Check entrypoint has command field (e.g., /skill/validate)
  • Ensure handler field 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

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