--- 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 ```bash python skills/plugin.sync/plugin_sync.py ``` No arguments required - reads from standard registry locations. ### Via Betty CLI ```bash /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: ```yaml - 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 ```json { "ok": true, "status": "success", "output_path": "/home/user/betty/plugin.yaml", "commands_generated": 18, "warnings": [] } ``` ### Response with Warnings ```json { "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 ```json { "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: ```yaml # 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: ```python 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` ```bash # 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 ```bash # 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 ```bash # 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: ```bash /skill/define skills/my.skill/skill.yaml /plugin/sync ``` ### With Workflows Include plugin sync as a workflow step: ```yaml # 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: ```yaml # .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 - **skill.define** – Validate and register skills ([SKILL.md](../skill.define/SKILL.md)) - **registry.update** – Update skill registry ([SKILL.md](../registry.update/SKILL.md)) - **skill.create** – Create new skills ([SKILL.md](../skill.create/SKILL.md)) - **Betty Architecture** – Framework overview ([betty-architecture.md](../../docs/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