diff --git a/app/src/promptforge/cli/commands/registry.py b/app/src/promptforge/cli/commands/registry.py new file mode 100644 index 0000000..a37ab89 --- /dev/null +++ b/app/src/promptforge/cli/commands/registry.py @@ -0,0 +1,109 @@ +import click + +from promptforge.registry import LocalRegistry, RemoteRegistry, RegistryEntry +from promptforge.core.prompt import Prompt + + +@click.group() +def registry(): + """Manage prompt registry.""" + pass + + +@registry.command("list") +@click.option("--tag", help="Filter by tag") +@click.option("--limit", default=20, help="Maximum results") +@click.pass_obj +def registry_list(ctx, tag: str, limit: int): + """List prompts in local registry.""" + registry = LocalRegistry() + entries = registry.list(tag=tag, limit=limit) + + if not entries: + click.echo("No prompts in registry") + return + + for entry in entries: + click.echo(f"{entry.name} (v{entry.version})") + if entry.description: + click.echo(f" {entry.description}") + + +@registry.command("add") +@click.argument("prompt_name") +@click.option("--author", help="Author name") +@click.pass_obj +def registry_add(ctx, prompt_name: str, author: str): + """Add a prompt to the local registry.""" + prompts_dir = ctx["prompts_dir"] + prompts = Prompt.list(prompts_dir) + + prompt = next((p for p in prompts if p.name == prompt_name), None) + if not prompt: + click.echo(f"Prompt '{prompt_name}' not found", err=True) + raise click.Abort() + + entry = RegistryEntry.from_prompt(prompt, author=author) + registry = LocalRegistry() + registry.add(entry) + + click.echo(f"Added '{prompt_name}' to registry") + + +@registry.command("search") +@click.argument("query") +@click.option("--limit", default=20, help="Maximum results") +@click.pass_obj +def registry_search(ctx, query: str, limit: int): + """Search local registry.""" + registry = LocalRegistry() + results = registry.search(query) + + if not results: + click.echo("No results found") + return + + for result in results[:limit]: + entry = result.entry + click.echo(f"{entry.name} (score: {result.relevance_score})") + if entry.description: + click.echo(f" {entry.description}") + + +@registry.command("pull") +@click.argument("entry_id") +@click.pass_obj +def registry_pull(ctx, entry_id: str): + """Pull a prompt from remote registry.""" + remote = RemoteRegistry() + local = LocalRegistry() + + if remote.pull(entry_id, local): + click.echo(f"Pulled entry {entry_id}") + else: + click.echo("Entry not found", err=True) + raise click.Abort() + + +@registry.command("publish") +@click.argument("prompt_name") +@click.pass_obj +def registry_publish(ctx, prompt_name: str): + """Publish a prompt to remote registry.""" + prompts_dir = ctx["prompts_dir"] + prompts = Prompt.list(prompts_dir) + + prompt = next((p for p in prompts if p.name == prompt_name), None) + if not prompt: + click.echo(f"Prompt '{prompt_name}' not found", err=True) + raise click.Abort() + + entry = RegistryEntry.from_prompt(prompt) + remote = RemoteRegistry() + + try: + published = remote.publish(entry) + click.echo(f"Published '{prompt_name}' as {published.id}") + except Exception as e: + click.echo(f"Publish error: {e}", err=True) + raise click.Abort()