Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:26:08 +08:00
commit 8f22ddf339
295 changed files with 59710 additions and 0 deletions

View File

@@ -0,0 +1,724 @@
---
name: Generate Marketplace
description: Generate marketplace catalog files from Betty Framework registries
---
# generate.marketplace
## Overview
**generate.marketplace** generates the RiskExec Claude Marketplace catalogs by filtering certified skills, agents, commands, and hooks from the Betty Framework registries. It transforms registry entries into marketplace-ready JSON files optimized for discovery and distribution.
## Purpose
Automates the generation of marketplace catalogs to maintain consistency between:
- **Skill Registry** (`registry/skills.json`) - All registered skills
- **Agent Registry** (`registry/agents.json`) - All registered agents
- **Command Registry** (`registry/commands.json`) - All registered commands
- **Hook Registry** (`registry/hooks.json`) - All registered hooks
- **Skills Marketplace** (`marketplace/skills.json`) - Certified skills for distribution
- **Agents Marketplace** (`marketplace/agents.json`) - Certified agents for distribution
- **Commands Marketplace** (`marketplace/commands.json`) - Certified commands for distribution
- **Hooks Marketplace** (`marketplace/hooks.json`) - Certified hooks for distribution
This eliminates manual curation of marketplace catalogs and ensures only production-ready, certified components are published.
## What It Does
1. **Reads Registries**: Loads `registry/skills.json`, `registry/agents.json`, `registry/commands.json`, and `registry/hooks.json`
2. **Filters Active Items**: Processes only entries with `status: active`
3. **Filters Certified Items**: Includes only entries with `certified: true` (if field exists)
4. **Transforms Format**: Converts registry entries to marketplace format
5. **Enriches Metadata**: Adds maintainer, usage examples, documentation URLs, and last_updated timestamps
6. **Generates Catalogs**: Outputs `marketplace/skills.json`, `marketplace/agents.json`, `marketplace/commands.json`, and `marketplace/hooks.json`
7. **Reports Statistics**: Shows certification counts and totals
## Usage
### Basic Usage
```bash
python skills/generate.marketplace/generate_marketplace.py
```
No arguments required - reads from standard registry locations.
### Via Betty CLI
```bash
/marketplace/generate
```
### Expected Directory Structure
```
betty/
├── registry/
│ ├── skills.json # Source: All registered skills
│ ├── agents.json # Source: All registered agents
│ ├── commands.json # Source: All registered commands
│ └── hooks.json # Source: All registered hooks
└── marketplace/
├── skills.json # Output: Certified skills only
├── agents.json # Output: Certified agents only
├── commands.json # Output: Certified commands only
└── hooks.json # Output: Certified hooks only
```
## Filtering Logic
### Certification Criteria
A skill, agent, command, or hook is included in the marketplace if:
1. **Status is Active**: `status: "active"`
2. **Certified Flag** (if present): `certified: true`
If the `certified` field is not present, active items are considered certified by default.
### Status Values
| Status | Included in Marketplace | Purpose |
|--------|------------------------|---------|
| `active` | Yes | Production-ready, certified items |
| `draft` | No | Work in progress, not ready for distribution |
| `deprecated` | No | Outdated, should not be used |
| `experimental` | No | Testing phase, unstable |
## Output Format
### Skills Marketplace Structure
```json
{
"marketplace_version": "1.0.0",
"generated_at": "2025-10-23T17:51:58.579847+00:00",
"last_updated": "2025-10-23T17:51:58.579847+00:00",
"description": "Betty Framework Certified Skills Marketplace",
"total_skills": 20,
"certified_count": 16,
"draft_count": 4,
"catalog": [
{
"name": "api.validate",
"version": "0.1.0",
"description": "Validate OpenAPI and AsyncAPI specifications",
"status": "certified",
"tags": ["api", "validation", "openapi"],
"maintainer": "Betty Core Team",
"usage_examples": [
"Validate OpenAPI spec: /skill/api/validate --spec_path api.yaml"
],
"documentation_url": "https://betty-framework.dev/docs/skills/api.validate",
"dependencies": ["context.schema"],
"entrypoints": [...],
"inputs": [...],
"outputs": [...]
}
]
}
```
### Agents Marketplace Structure
```json
{
"marketplace_version": "1.0.0",
"generated_at": "2025-10-23T17:03:16.154165+00:00",
"last_updated": "2025-10-23T17:03:16.154165+00:00",
"description": "Betty Framework Certified Agents Marketplace",
"total_agents": 5,
"certified_count": 3,
"draft_count": 2,
"catalog": [
{
"name": "api.designer",
"version": "0.1.0",
"description": "Design RESTful APIs following enterprise guidelines",
"status": "certified",
"reasoning_mode": "iterative",
"skills_available": ["api.define", "api.validate"],
"capabilities": [
"Design RESTful APIs from natural language requirements"
],
"tags": ["api", "design", "openapi"],
"maintainer": "Betty Core Team",
"documentation_url": "https://betty-framework.dev/docs/agents/api.designer",
"dependencies": ["context.schema"]
}
]
}
```
### Commands Marketplace Structure
```json
{
"marketplace_version": "1.0.0",
"generated_at": "2025-10-23T17:51:58.579847+00:00",
"last_updated": "2025-10-23T17:51:58.579847+00:00",
"description": "Betty Framework Certified Commands Marketplace",
"total_commands": 4,
"certified_count": 1,
"draft_count": 3,
"catalog": [
{
"name": "/test-workflow-command",
"version": "1.0.0",
"description": "Test complete workflow",
"status": "certified",
"tags": ["test", "workflow"],
"execution": {
"type": "skill",
"target": "api.validate"
},
"parameters": [
{
"name": "input",
"type": "string",
"required": true,
"description": "Input parameter"
}
],
"maintainer": "Betty Core Team"
}
]
}
```
### Hooks Marketplace Structure
```json
{
"marketplace_version": "1.0.0",
"generated_at": "2025-10-23T17:51:58.579847+00:00",
"last_updated": "2025-10-23T17:51:58.579847+00:00",
"description": "Betty Framework Certified Hooks Marketplace",
"total_hooks": 4,
"certified_count": 1,
"draft_count": 3,
"catalog": [
{
"name": "test-workflow-hook",
"version": "1.0.0",
"description": "Test complete workflow",
"status": "certified",
"tags": ["test", "workflow", "openapi"],
"event": "on_file_edit",
"command": "python validate.py {file_path}",
"blocking": true,
"when": {
"pattern": "*.openapi.yaml"
},
"timeout": 30000,
"on_failure": "show_errors",
"maintainer": "Betty Core Team"
}
]
}
```
## Marketplace Transformations
### From Registry to Marketplace
The skill transforms registry entries to marketplace format:
#### Skills and Agents
| Registry Field | Marketplace Field | Transformation |
|----------------|-------------------|----------------|
| `status: "active"` | `status: "certified"` | Renamed for marketplace context |
| `name` | `name` | Preserved |
| `version` | `version` | Preserved |
| `description` | `description` | Preserved |
| `tags` | `tags` | Preserved (default: `[]`) |
| `dependencies` | `dependencies` | Preserved (default: `[]`) |
| `entrypoints` | `entrypoints` | Preserved (skills only) |
| `inputs` | `inputs` | Preserved (skills only) |
| `outputs` | `outputs` | Preserved (skills only) |
| `skills_available` | `skills_available` | Preserved (agents only) |
| `capabilities` | `capabilities` | Preserved (agents only) |
| `reasoning_mode` | `reasoning_mode` | Preserved (agents only) |
| N/A | `maintainer` | Added (default: "Betty Core Team") |
| N/A | `usage_examples` | Generated from entrypoints or provided |
| N/A | `documentation_url` | Generated: `https://betty-framework.dev/docs/{type}/{name}` |
| N/A | `last_updated` | Added: ISO timestamp |
#### Commands
| Registry Field | Marketplace Field | Transformation |
|----------------|-------------------|----------------|
| `status: "active"` | `status: "certified"` | Renamed for marketplace context |
| `name` | `name` | Preserved |
| `version` | `version` | Preserved |
| `description` | `description` | Preserved |
| `tags` | `tags` | Preserved (default: `[]`) |
| `execution` | `execution` | Preserved |
| `parameters` | `parameters` | Preserved (default: `[]`) |
| N/A | `maintainer` | Added (default: "Betty Core Team") |
| N/A | `last_updated` | Added: ISO timestamp |
#### Hooks
| Registry Field | Marketplace Field | Transformation |
|----------------|-------------------|----------------|
| `status: "active"` | `status: "certified"` | Renamed for marketplace context |
| `name` | `name` | Preserved |
| `version` | `version` | Preserved |
| `description` | `description` | Preserved |
| `tags` | `tags` | Preserved (default: `[]`) |
| `event` | `event` | Preserved |
| `command` | `command` | Preserved |
| `blocking` | `blocking` | Preserved (default: `false`) |
| `when` | `when` | Preserved (default: `{}`) |
| `timeout` | `timeout` | Preserved |
| `on_failure` | `on_failure` | Preserved |
| N/A | `maintainer` | Added (default: "Betty Core Team") |
| N/A | `last_updated` | Added: ISO timestamp |
### Metadata Enrichment
The skill adds marketplace-specific metadata:
1. **Maintainer**: Defaults to "Betty Core Team" if not specified
2. **Usage Examples**: Auto-generated from entrypoint commands if missing (skills only)
3. **Documentation URL**: Generated following the pattern `https://betty-framework.dev/docs/{skills|agents}/{name}` (skills and agents only)
4. **Last Updated**: ISO timestamp added to all marketplace files
5. **Statistics**: Adds total counts, certified counts, and draft counts
## Behavior
### 1. Registry Loading
Reads JSON files from:
- `registry/skills.json`
- `registry/agents.json`
- `registry/commands.json`
- `registry/hooks.json`
If a registry file is missing, the skill fails with an error.
### 2. Filtering
For each skill/agent/command/hook in the registry:
- Checks `status` field - must be `"active"`
- Checks `certified` field (if present) - must be `true`
- Skips items that don't meet criteria
- Logs which items are included/excluded
### 3. Transformation
Converts each certified entry:
- Copies core fields (name, version, description, tags)
- Transforms `status: "active"``status: "certified"`
- Adds marketplace metadata (maintainer, last_updated timestamp)
- For skills: Adds docs URL and generates usage examples if not provided
- For agents: Adds docs URL
- Preserves all technical details (entrypoints, inputs, outputs, execution, parameters, event, command, etc.)
### 4. Statistics Calculation
Tracks:
- **Total items**: All items in registry
- **Certified count**: Items included in marketplace
- **Draft count**: Items excluded (total - certified)
### 5. File Writing
Writes marketplace catalogs:
- Creates `marketplace/` directory if needed
- Formats JSON with 2-space indentation
- Preserves Unicode characters (no ASCII escaping)
- Adds generation timestamp
## Outputs
### Success Response
```json
{
"ok": true,
"status": "success",
"skills_output": "/home/user/betty/marketplace/skills.json",
"agents_output": "/home/user/betty/marketplace/agents.json",
"commands_output": "/home/user/betty/marketplace/commands.json",
"hooks_output": "/home/user/betty/marketplace/hooks.json",
"skills_certified": 16,
"skills_total": 20,
"agents_certified": 3,
"agents_total": 5,
"commands_certified": 1,
"commands_total": 4,
"hooks_certified": 1,
"hooks_total": 4
}
```
### Failure Response
```json
{
"ok": false,
"status": "failed",
"error": "Registry file not found: /home/user/betty/registry/skills.json"
}
```
## Examples
### Example 1: Basic Marketplace Generation
**Scenario**: Generate marketplace catalogs after adding new certified skills
```bash
# Register new skills
/skill/define skills/data.transform/skill.yaml
/skill/define skills/api.monitor/skill.yaml
# Update registry
/registry/update
# Generate marketplace
/marketplace/generate
```
**Output**:
```
INFO: Starting marketplace catalog generation from registries...
INFO: Loading registry files...
INFO: Generating marketplace catalogs...
INFO: Added certified skill: api.validate
INFO: Added certified skill: api.define
INFO: Skipped non-certified skill: test.hello (status: draft)
INFO: Added certified agent: api.designer
INFO: Added certified command: /test-workflow-command
INFO: Skipped non-certified command: /test-command (status: draft)
INFO: Added certified hook: test-workflow-hook
INFO: Skipped non-certified hook: test-validation-hook (status: draft)
INFO: Writing marketplace files...
INFO: ✅ Written marketplace file to /home/user/betty/marketplace/skills.json
INFO: ✅ Written marketplace file to /home/user/betty/marketplace/agents.json
INFO: ✅ Written marketplace file to /home/user/betty/marketplace/commands.json
INFO: ✅ Written marketplace file to /home/user/betty/marketplace/hooks.json
INFO: ✅ Generated marketplace catalogs:
INFO: Skills: 16/20 certified
INFO: Agents: 3/5 certified
INFO: Commands: 1/4 certified
INFO: Hooks: 1/4 certified
```
### Example 2: After Promoting Skills to Active
**Scenario**: Skills were marked as active and should now appear in marketplace
```bash
# Edit registry to mark skills as active
# (Normally done via skill.define)
# Regenerate marketplace
/marketplace/generate
```
**Before** (registry):
```json
{
"name": "my.skill",
"status": "draft"
}
```
**After** (registry updated):
```json
{
"name": "my.skill",
"status": "active"
}
```
**Marketplace** (now includes):
```json
{
"name": "my.skill",
"status": "certified"
}
```
### Example 3: Publishing to GitHub Pages
**Scenario**: Deploy marketplace catalogs to public API endpoint
```bash
# Generate marketplace
/marketplace/generate
# Copy to GitHub Pages directory
cp marketplace/*.json docs/api/v1/
# Commit and push
git add marketplace/ docs/api/v1/
git commit -m "Update marketplace catalog"
git push
```
Now accessible at:
- `https://riskexec.github.io/betty/api/v1/skills.json`
- `https://riskexec.github.io/betty/api/v1/agents.json`
- `https://riskexec.github.io/betty/api/v1/commands.json`
- `https://riskexec.github.io/betty/api/v1/hooks.json`
### Example 4: CI/CD Integration
**Scenario**: Auto-generate marketplace on every registry change
```yaml
# .github/workflows/marketplace.yml
name: Update Marketplace
on:
push:
paths:
- 'registry/*.json'
jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Generate Marketplace
run: python skills/generate.marketplace/generate_marketplace.py
- name: Commit Changes
run: |
git config user.name "Betty Bot"
git config user.email "bot@riskexec.com"
git add marketplace/
git commit -m "chore: update marketplace catalog"
git push
```
## Integration
### With skill.define Workflow
After registering skills, regenerate marketplace:
```bash
/skill/define skills/new.skill/skill.yaml
/registry/update
/marketplace/generate
```
### With Workflows
Include marketplace generation 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: registry.update
args: ["skills/new.skill/skill.yaml"]
- skill: generate.marketplace
args: []
```
### With Hooks
Auto-regenerate marketplace when registries change:
```yaml
# .claude/hooks.yaml
- event: on_file_save
pattern: "registry/*.json"
command: python skills/generate.marketplace/generate_marketplace.py
blocking: false
description: Auto-regenerate marketplace when registry changes
```
## What Gets Included
### Included in Marketplace
- Skills/agents/commands/hooks with `status: "active"`
- Skills/agents/commands/hooks with `certified: true` (if field exists)
- All technical metadata (entrypoints, inputs, outputs, execution, parameters, event, command, etc.)
- All semantic metadata (tags, dependencies)
- Last updated timestamp for all entries
### Not Included
- Skills/agents/commands/hooks with `status: "draft"`
- Skills/agents/commands/hooks with `status: "deprecated"`
- Skills/agents/commands/hooks with `certified: false`
- Internal-only items
- Test/experimental items (unless marked active)
## Common Errors
| Error | Cause | Solution |
|-------|-------|----------|
| "Registry file not found" | Missing registry file | Ensure `registry/skills.json` and `registry/agents.json` exist |
| "Failed to parse JSON" | Invalid JSON syntax | Fix JSON syntax in registry files |
| "Permission denied" | Cannot write marketplace files | Check write permissions on `marketplace/` directory |
| Empty marketplace | No active skills | Mark skills as `status: "active"` in registry |
## Files Read
- `registry/skills.json` - Skill registry (source)
- `registry/agents.json` - Agent registry (source)
- `registry/commands.json` - Command registry (source)
- `registry/hooks.json` - Hook registry (source)
## Files Modified
- `marketplace/skills.json` - Skills marketplace catalog (output)
- `marketplace/agents.json` - Agents marketplace catalog (output)
- `marketplace/commands.json` - Commands marketplace catalog (output)
- `marketplace/hooks.json` - Hooks marketplace catalog (output)
## Exit Codes
- **0**: Success (marketplace catalogs generated successfully)
- **1**: Failure (error during generation)
## Logging
Logs generation progress:
```
INFO: Starting marketplace catalog generation from registries...
INFO: Loading registry files...
INFO: Generating marketplace catalogs...
INFO: Added certified skill: api.validate
INFO: Added certified skill: hook.define
DEBUG: Skipped non-certified skill: test.hello (status: draft)
INFO: Added certified agent: api.designer
INFO: Added certified command: /test-workflow-command
DEBUG: Skipped non-certified command: /test-command (status: draft)
INFO: Added certified hook: test-workflow-hook
DEBUG: Skipped non-certified hook: test-validation-hook (status: draft)
INFO: Writing marketplace files...
INFO: ✅ Written marketplace file to marketplace/skills.json
INFO: ✅ Written marketplace file to marketplace/agents.json
INFO: ✅ Written marketplace file to marketplace/commands.json
INFO: ✅ Written marketplace file to marketplace/hooks.json
INFO: ✅ Generated marketplace catalogs:
INFO: Skills: 16/20 certified
INFO: Agents: 3/5 certified
INFO: Commands: 1/4 certified
INFO: Hooks: 1/4 certified
```
## Best Practices
1. **Run After Registry Updates**: Regenerate marketplace after adding/updating skills
2. **Automate with CI/CD**: Set up automated marketplace generation in pipelines
3. **Review Before Publishing**: Check generated catalogs before deploying
4. **Version Control**: Commit marketplace files with registry changes
5. **Keep Registries Clean**: Remove deprecated skills to keep marketplace focused
6. **Document Thoroughly**: Ensure skills have good descriptions and examples
## Troubleshooting
### Marketplace Files Not Updating
**Problem**: Changes to registry don't appear in marketplace
**Solutions**:
- Ensure skill status is `"active"` in registry
- Check that `certified` field is `true` (if present)
- Run `/registry/update` before `/marketplace/generate`
- Verify registry JSON syntax is valid
### Skills Missing from Marketplace
**Problem**: Active skills not appearing in marketplace
**Solutions**:
- Check skill status in `registry/skills.json`
- Verify no `certified: false` field
- Ensure skill.yaml has been validated with `/skill/define`
- Check logs for filtering messages
### Empty Marketplace Catalogs
**Problem**: Marketplace has 0 certified items
**Solutions**:
- Mark skills as `status: "active"` in registry
- Remove `certified: false` from skill entries
- Ensure registry files are not empty
- Run `/skill/define` to register skills first
## Version Diff (Optional)
To add version diff vs. last release:
```python
# Future enhancement
def get_version_diff(old_marketplace, new_marketplace):
"""Compare two marketplace versions and return diff."""
added = [s for s in new if s not in old]
removed = [s for s in old if s not in new]
updated = [s for s in new if s in old and version_changed(s)]
return {"added": added, "removed": removed, "updated": updated}
```
## Upload to API (Optional)
To upload generated catalogs to an API:
```python
# Future enhancement
import requests
def upload_to_api(marketplace_data, api_endpoint, api_key):
"""Upload marketplace catalog to internal API."""
response = requests.post(
api_endpoint,
json=marketplace_data,
headers={"Authorization": f"Bearer {api_key}"}
)
return response.status_code == 200
```
## Architecture
### Skill Categories
**Infrastructure** - generate.marketplace maintains the marketplace layer by transforming registry state into certified catalogs.
### Design Principles
- **Single Source of Truth**: Registry files are the source
- **Idempotent**: Can be run multiple times safely
- **Certification Filter**: Only production-ready items included
- **Metadata Enrichment**: Adds marketplace-specific fields
- **Clear Statistics**: Reports certification rates
## See Also
- **plugin.sync** - Generate plugin.yaml from registries ([SKILL.md](../plugin.sync/SKILL.md))
- **registry.update** - Update skill registry ([SKILL.md](../registry.update/SKILL.md))
- **skill.define** - Validate and register skills ([SKILL.md](../skill.define/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.2.0** (Oct 2025) - Added support for commands and hooks, added last_updated timestamps
- **0.1.0** (Oct 2025) - Initial implementation with filtering and marketplace generation for skills and agents

View File

@@ -0,0 +1 @@
# Auto-generated package initializer for skills.

View File

@@ -0,0 +1,522 @@
#!/usr/bin/env python3
"""
generate_marketplace.py - Implementation of the generate.marketplace Skill
Generates marketplace JSON files from registry entries:
- marketplace/skills.json
- marketplace/agents.json
- marketplace/commands.json
- marketplace/hooks.json
Filters by status: active and certified: true (if present).
Adds last_updated ISO timestamp to all marketplace files.
"""
import os
import sys
import json
from typing import Dict, Any, List, Optional
from datetime import datetime, timezone
from pathlib import Path
from betty.config import BASE_DIR
from betty.logging_utils import setup_logger
logger = setup_logger(__name__)
def load_registry_file(registry_path: str) -> Dict[str, Any]:
"""
Load a JSON registry file.
Args:
registry_path: Path to the registry JSON file
Returns:
Parsed registry data
Raises:
FileNotFoundError: If registry file doesn't exist
json.JSONDecodeError: If JSON is invalid
"""
try:
with open(registry_path) as f:
return json.load(f)
except FileNotFoundError:
logger.error(f"Registry file not found: {registry_path}")
raise
except json.JSONDecodeError as e:
logger.error(f"Failed to parse JSON from {registry_path}: {e}")
raise
def is_certified(item: Dict[str, Any]) -> bool:
"""
Check if an item is certified for marketplace inclusion.
Args:
item: Skill or agent entry
Returns:
True if item should be included in marketplace
"""
# Filter by status: active
if item.get("status") != "active":
return False
# If certified field exists, check it
if "certified" in item:
return item.get("certified") is True
# If no certified field, consider active items as certified
return True
def convert_skill_to_marketplace(skill: Dict[str, Any]) -> Dict[str, Any]:
"""
Convert a registry skill entry to marketplace format.
Args:
skill: Skill entry from registry
Returns:
Skill entry in marketplace format
"""
skill_name = skill.get("name")
marketplace_skill = {
"name": skill_name,
"version": skill.get("version"),
"description": skill.get("description"),
"status": "certified", # Transform active -> certified for marketplace
"tags": skill.get("tags", []),
"manifest_path": f"skills/{skill_name}/skill.yaml",
"maintainer": skill.get("maintainer", "Betty Core Team"),
"usage_examples": skill.get("usage_examples", []),
"documentation_url": f"https://betty-framework.dev/docs/skills/{skill_name}",
"dependencies": skill.get("dependencies", []),
"entrypoints": skill.get("entrypoints", []),
"inputs": skill.get("inputs", []),
"outputs": skill.get("outputs", [])
}
# Generate usage examples if not present
if not marketplace_skill["usage_examples"] and marketplace_skill["entrypoints"]:
examples = []
for entrypoint in marketplace_skill["entrypoints"]:
command = entrypoint.get("command", "")
desc = entrypoint.get("description", skill.get("description", ""))
if command:
# Create a simple example from the command
example = f"Run {skill.get('name')}: {command}"
examples.append(example.strip())
marketplace_skill["usage_examples"] = examples[:2] # Limit to 2 examples
return marketplace_skill
def convert_agent_to_marketplace(agent: Dict[str, Any]) -> Dict[str, Any]:
"""
Convert a registry agent entry to marketplace format.
Args:
agent: Agent entry from registry
Returns:
Agent entry in marketplace format
"""
agent_name = agent.get("name")
marketplace_agent = {
"name": agent_name,
"version": agent.get("version"),
"description": agent.get("description"),
"status": "certified", # Transform active -> certified for marketplace
"reasoning_mode": agent.get("reasoning_mode", "oneshot"),
"skills_available": agent.get("skills_available", []),
"capabilities": agent.get("capabilities", []),
"tags": agent.get("tags", []),
"manifest_path": f"agents/{agent_name}/agent.yaml",
"maintainer": agent.get("maintainer", "Betty Core Team"),
"documentation_url": f"https://betty-framework.dev/docs/agents/{agent_name}",
"dependencies": agent.get("dependencies", [])
}
return marketplace_agent
def convert_command_to_marketplace(command: Dict[str, Any]) -> Dict[str, Any]:
"""
Convert a registry command entry to marketplace format.
Args:
command: Command entry from registry
Returns:
Command entry in marketplace format
"""
marketplace_command = {
"name": command.get("name"),
"version": command.get("version"),
"description": command.get("description"),
"status": "certified", # Transform active -> certified for marketplace
"tags": command.get("tags", []),
"execution": command.get("execution", {}),
"parameters": command.get("parameters", []),
"maintainer": command.get("maintainer", "Betty Core Team")
}
return marketplace_command
def convert_hook_to_marketplace(hook: Dict[str, Any]) -> Dict[str, Any]:
"""
Convert a registry hook entry to marketplace format.
Args:
hook: Hook entry from registry
Returns:
Hook entry in marketplace format
"""
marketplace_hook = {
"name": hook.get("name"),
"version": hook.get("version"),
"description": hook.get("description"),
"status": "certified", # Transform active -> certified for marketplace
"tags": hook.get("tags", []),
"event": hook.get("event"),
"command": hook.get("command"),
"blocking": hook.get("blocking", False),
"when": hook.get("when", {}),
"timeout": hook.get("timeout"),
"on_failure": hook.get("on_failure"),
"maintainer": hook.get("maintainer", "Betty Core Team")
}
return marketplace_hook
def generate_skills_marketplace(registry_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Generate marketplace skills catalog from registry.
Args:
registry_data: Parsed skills.json from registry
Returns:
Marketplace-formatted skills catalog
"""
skills = registry_data.get("skills", [])
# Filter and convert active/certified skills
certified_skills = []
for skill in skills:
if is_certified(skill):
marketplace_skill = convert_skill_to_marketplace(skill)
certified_skills.append(marketplace_skill)
logger.info(f"Added certified skill: {skill.get('name')}")
else:
logger.debug(f"Skipped non-certified skill: {skill.get('name')} (status: {skill.get('status')})")
# Build marketplace catalog
marketplace = {
"marketplace_version": "1.0.0",
"generated_at": datetime.now(timezone.utc).isoformat(),
"last_updated": datetime.now(timezone.utc).isoformat(),
"description": "Betty Framework Certified Skills Marketplace",
"total_skills": len(skills),
"certified_count": len(certified_skills),
"draft_count": len(skills) - len(certified_skills),
"catalog": certified_skills
}
return marketplace
def generate_agents_marketplace(registry_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Generate marketplace agents catalog from registry.
Args:
registry_data: Parsed agents.json from registry
Returns:
Marketplace-formatted agents catalog
"""
agents = registry_data.get("agents", [])
# Filter and convert active/certified agents
certified_agents = []
for agent in agents:
if is_certified(agent):
marketplace_agent = convert_agent_to_marketplace(agent)
certified_agents.append(marketplace_agent)
logger.info(f"Added certified agent: {agent.get('name')}")
else:
logger.debug(f"Skipped non-certified agent: {agent.get('name')} (status: {agent.get('status')})")
# Build marketplace catalog
marketplace = {
"marketplace_version": "1.0.0",
"generated_at": datetime.now(timezone.utc).isoformat(),
"last_updated": datetime.now(timezone.utc).isoformat(),
"description": "Betty Framework Certified Agents Marketplace",
"total_agents": len(agents),
"certified_count": len(certified_agents),
"draft_count": len(agents) - len(certified_agents),
"catalog": certified_agents
}
return marketplace
def generate_commands_marketplace(registry_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Generate marketplace commands catalog from registry.
Args:
registry_data: Parsed commands.json from registry
Returns:
Marketplace-formatted commands catalog
"""
commands = registry_data.get("commands", [])
# Filter and convert active/certified commands
certified_commands = []
for command in commands:
if is_certified(command):
marketplace_command = convert_command_to_marketplace(command)
certified_commands.append(marketplace_command)
logger.info(f"Added certified command: {command.get('name')}")
else:
logger.debug(f"Skipped non-certified command: {command.get('name')} (status: {command.get('status')})")
# Build marketplace catalog
marketplace = {
"marketplace_version": "1.0.0",
"generated_at": datetime.now(timezone.utc).isoformat(),
"last_updated": datetime.now(timezone.utc).isoformat(),
"description": "Betty Framework Certified Commands Marketplace",
"total_commands": len(commands),
"certified_count": len(certified_commands),
"draft_count": len(commands) - len(certified_commands),
"catalog": certified_commands
}
return marketplace
def generate_hooks_marketplace(registry_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Generate marketplace hooks catalog from registry.
Args:
registry_data: Parsed hooks.json from registry
Returns:
Marketplace-formatted hooks catalog
"""
hooks = registry_data.get("hooks", [])
# Filter and convert active/certified hooks
certified_hooks = []
for hook in hooks:
if is_certified(hook):
marketplace_hook = convert_hook_to_marketplace(hook)
certified_hooks.append(marketplace_hook)
logger.info(f"Added certified hook: {hook.get('name')}")
else:
logger.debug(f"Skipped non-certified hook: {hook.get('name')} (status: {hook.get('status')})")
# Build marketplace catalog
marketplace = {
"marketplace_version": "1.0.0",
"generated_at": datetime.now(timezone.utc).isoformat(),
"last_updated": datetime.now(timezone.utc).isoformat(),
"description": "Betty Framework Certified Hooks Marketplace",
"total_hooks": len(hooks),
"certified_count": len(certified_hooks),
"draft_count": len(hooks) - len(certified_hooks),
"catalog": certified_hooks
}
return marketplace
def write_marketplace_file(data: Dict[str, Any], output_path: str):
"""
Write marketplace JSON file with proper formatting.
Args:
data: Marketplace data dictionary
output_path: Path where to write the file
"""
os.makedirs(os.path.dirname(output_path), exist_ok=True)
with open(output_path, 'w') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
logger.info(f"✅ Written marketplace file to {output_path}")
def generate_claude_code_marketplace() -> Dict[str, Any]:
"""
Generate Claude Code marketplace.json file.
This file lists Betty Framework as an installable plugin in Claude Code's
marketplace format, as specified in Claude Code's plugin marketplace documentation.
Returns:
Claude Code marketplace JSON structure
"""
# Load plugin.yaml to get current metadata
plugin_yaml_path = os.path.join(BASE_DIR, "plugin.yaml")
try:
import yaml
with open(plugin_yaml_path, 'r') as f:
plugin_data = yaml.safe_load(f)
except Exception as e:
logger.warning(f"Could not load plugin.yaml: {e}. Using defaults.")
plugin_data = {}
# Build Claude Code marketplace structure
marketplace = {
"name": "betty-marketplace",
"version": plugin_data.get("version", "1.0.0"),
"description": "Betty Framework Plugin Marketplace - Enterprise-grade AI-assisted engineering framework",
"owner": {
"name": plugin_data.get("author", {}).get("name", "RiskExec"),
"email": plugin_data.get("author", {}).get("email", "platform@riskexec.com"),
"url": plugin_data.get("author", {}).get("url", "https://github.com/epieczko/betty")
},
"metadata": {
"homepage": plugin_data.get("metadata", {}).get("homepage", "https://github.com/epieczko/betty"),
"repository": plugin_data.get("metadata", {}).get("repository", "https://github.com/epieczko/betty"),
"documentation": plugin_data.get("metadata", {}).get("documentation", "https://github.com/epieczko/betty/tree/main/docs"),
"generated_at": datetime.now(timezone.utc).isoformat(),
"generated_by": "generate.marketplace skill"
},
"plugins": [
{
"name": plugin_data.get("name", "betty-framework"),
"source": ".",
"version": plugin_data.get("version", "1.0.0"),
"description": plugin_data.get("description", "Betty Framework for structured AI-assisted engineering"),
"author": {
"name": plugin_data.get("author", {}).get("name", "RiskExec"),
"email": plugin_data.get("author", {}).get("email", "platform@riskexec.com"),
"url": plugin_data.get("author", {}).get("url", "https://github.com/epieczko/betty")
},
"license": plugin_data.get("license", "MIT"),
"tags": plugin_data.get("metadata", {}).get("tags", [
"framework",
"api-development",
"workflow",
"governance",
"enterprise"
]),
"requirements": plugin_data.get("requirements", {
"python": ">=3.11",
"packages": ["pyyaml"]
}),
"strict": True # Requires plugin.yaml manifest
}
]
}
return marketplace
def main():
"""Main CLI entry point."""
logger.info("Starting marketplace catalog generation from registries...")
# Define registry and output paths
skills_registry_path = os.path.join(BASE_DIR, "registry", "skills.json")
agents_registry_path = os.path.join(BASE_DIR, "registry", "agents.json")
commands_registry_path = os.path.join(BASE_DIR, "registry", "commands.json")
hooks_registry_path = os.path.join(BASE_DIR, "registry", "hooks.json")
marketplace_dir = os.path.join(BASE_DIR, "marketplace")
skills_output_path = os.path.join(marketplace_dir, "skills.json")
agents_output_path = os.path.join(marketplace_dir, "agents.json")
commands_output_path = os.path.join(marketplace_dir, "commands.json")
hooks_output_path = os.path.join(marketplace_dir, "hooks.json")
try:
# Load registry files
logger.info("Loading registry files...")
skills_registry = load_registry_file(skills_registry_path)
agents_registry = load_registry_file(agents_registry_path)
commands_registry = load_registry_file(commands_registry_path)
hooks_registry = load_registry_file(hooks_registry_path)
# Generate marketplace catalogs
logger.info("Generating marketplace catalogs...")
skills_marketplace = generate_skills_marketplace(skills_registry)
agents_marketplace = generate_agents_marketplace(agents_registry)
commands_marketplace = generate_commands_marketplace(commands_registry)
hooks_marketplace = generate_hooks_marketplace(hooks_registry)
# Write Betty marketplace catalogs to files
logger.info("Writing Betty marketplace files...")
write_marketplace_file(skills_marketplace, skills_output_path)
write_marketplace_file(agents_marketplace, agents_output_path)
write_marketplace_file(commands_marketplace, commands_output_path)
write_marketplace_file(hooks_marketplace, hooks_output_path)
# Generate and write Claude Code marketplace.json
logger.info("Generating Claude Code marketplace.json...")
claude_marketplace = generate_claude_code_marketplace()
claude_marketplace_path = os.path.join(BASE_DIR, ".claude-plugin", "marketplace.json")
write_marketplace_file(claude_marketplace, claude_marketplace_path)
# Report results
result = {
"ok": True,
"status": "success",
"skills_output": skills_output_path,
"agents_output": agents_output_path,
"commands_output": commands_output_path,
"hooks_output": hooks_output_path,
"claude_marketplace_output": claude_marketplace_path,
"skills_certified": skills_marketplace["certified_count"],
"skills_total": skills_marketplace["total_skills"],
"agents_certified": agents_marketplace["certified_count"],
"agents_total": agents_marketplace["total_agents"],
"commands_certified": commands_marketplace["certified_count"],
"commands_total": commands_marketplace["total_commands"],
"hooks_certified": hooks_marketplace["certified_count"],
"hooks_total": hooks_marketplace["total_hooks"]
}
# Print summary
logger.info(f"✅ Generated marketplace catalogs:")
logger.info(f" Skills: {result['skills_certified']}/{result['skills_total']} certified")
logger.info(f" Agents: {result['agents_certified']}/{result['agents_total']} certified")
logger.info(f" Commands: {result['commands_certified']}/{result['commands_total']} certified")
logger.info(f" Hooks: {result['hooks_certified']}/{result['hooks_total']} certified")
logger.info(f"📄 Outputs:")
logger.info(f" - {skills_output_path}")
logger.info(f" - {agents_output_path}")
logger.info(f" - {commands_output_path}")
logger.info(f" - {hooks_output_path}")
logger.info(f" - {claude_marketplace_path} (Claude Code format)")
print(json.dumps(result, indent=2))
sys.exit(0)
except Exception as e:
logger.error(f"Failed to generate marketplace catalogs: {e}")
result = {
"ok": False,
"status": "failed",
"error": str(e)
}
print(json.dumps(result, indent=2))
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,37 @@
name: generate.marketplace
version: 0.3.0
description: >
Generate marketplace catalog files from Betty Framework registries.
Filters active and certified skills/agents/commands/hooks and outputs marketplace-ready JSON files
with last_updated timestamps. Also generates Claude Code marketplace.json format.
inputs: []
outputs:
- marketplace/skills.json
- marketplace/agents.json
- marketplace/commands.json
- marketplace/hooks.json
- .claude-plugin/marketplace.json
dependencies:
- registry.update
status: active
entrypoints:
- command: /marketplace/generate
handler: generate_marketplace.py
runtime: python
description: >
Generate marketplace catalogs from registry files. Filters by status: active and certified: true.
Outputs skills, agents, commands, and hooks marketplace files with last_updated timestamps.
Also generates .claude-plugin/marketplace.json in Claude Code marketplace format.
parameters: []
permissions:
- filesystem:read
- filesystem:write
tags:
- marketplace
- registry
- automation
- infrastructure
- commands
- hooks