Add enhanced search and filtering (M2 feature)
- Add /api/v1/tags endpoint for listing available tags - Enhance search API with tag filtering (AND logic), multi-category filtering (OR logic), owner filter, download range, and date range - Add faceted response support (category/tag/owner counts) - Update registry client with new search parameters and get_tags method - Add CLI search options: -t/--tag, -o/--owner, --min-downloads, --popular, --new, --since, --before, --json, --show-facets - Add new 'registry tags' CLI subcommand - Add web UI filter sidebar with checkboxes, dropdowns, and active filter chips with URL-based state management - Display clickable tags on tool cards Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"""Registry commands."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -15,6 +16,8 @@ def cmd_registry(args):
|
||||
|
||||
if args.registry_cmd == "search":
|
||||
return _cmd_registry_search(args)
|
||||
elif args.registry_cmd == "tags":
|
||||
return _cmd_registry_tags(args)
|
||||
elif args.registry_cmd == "install":
|
||||
return _cmd_registry_install(args)
|
||||
elif args.registry_cmd == "uninstall":
|
||||
@@ -33,6 +36,7 @@ def cmd_registry(args):
|
||||
# Default: show registry help
|
||||
print("Registry commands:")
|
||||
print(" search <query> Search for tools")
|
||||
print(" tags List available tags")
|
||||
print(" install <tool> Install a tool")
|
||||
print(" uninstall <tool> Uninstall a tool")
|
||||
print(" info <tool> Show tool information")
|
||||
@@ -49,27 +53,73 @@ def _cmd_registry_search(args):
|
||||
|
||||
try:
|
||||
client = get_client()
|
||||
|
||||
# Handle shortcut flags
|
||||
min_downloads = getattr(args, 'min_downloads', None)
|
||||
max_downloads = None
|
||||
if getattr(args, 'popular', False):
|
||||
min_downloads = 100
|
||||
if getattr(args, 'new', False):
|
||||
max_downloads = 10
|
||||
|
||||
results = client.search_tools(
|
||||
query=args.query,
|
||||
category=args.category,
|
||||
per_page=args.limit or 20
|
||||
category=getattr(args, 'category', None),
|
||||
tags=getattr(args, 'tags', None),
|
||||
owner=getattr(args, 'owner', None),
|
||||
min_downloads=min_downloads,
|
||||
max_downloads=max_downloads,
|
||||
published_after=getattr(args, 'since', None),
|
||||
published_before=getattr(args, 'before', None),
|
||||
include_deprecated=getattr(args, 'deprecated', False),
|
||||
include_facets=getattr(args, 'show_facets', False),
|
||||
per_page=args.limit or 20,
|
||||
sort=getattr(args, 'sort', 'relevance')
|
||||
)
|
||||
|
||||
# JSON output
|
||||
if getattr(args, 'json', False):
|
||||
output = {
|
||||
"query": args.query,
|
||||
"total": results.total,
|
||||
"results": results.data
|
||||
}
|
||||
if results.facets:
|
||||
output["facets"] = results.facets
|
||||
print(json.dumps(output, indent=2))
|
||||
return 0
|
||||
|
||||
if not results.data:
|
||||
print(f"No tools found matching '{args.query}'")
|
||||
return 0
|
||||
|
||||
print(f"Found {results.total} tools:\n")
|
||||
print(f"Found {results.total} tools matching \"{args.query}\":")
|
||||
|
||||
# Show facets summary if requested
|
||||
if results.facets:
|
||||
cats = results.facets.get("categories", [])[:5]
|
||||
tags = results.facets.get("tags", [])[:5]
|
||||
if cats:
|
||||
cat_str = ", ".join(f"{c['name']} ({c['count']})" for c in cats)
|
||||
print(f"\nCategories: {cat_str}")
|
||||
if tags:
|
||||
tag_str = ", ".join(f"{t['name']} ({t['count']})" for t in tags)
|
||||
print(f"Top Tags: {tag_str}")
|
||||
|
||||
print()
|
||||
for tool in results.data:
|
||||
owner = tool.get("owner", "")
|
||||
name = tool.get("name", "")
|
||||
version = tool.get("version", "")
|
||||
desc = tool.get("description", "")
|
||||
downloads = tool.get("downloads", 0)
|
||||
tags = tool.get("tags", [])
|
||||
|
||||
print(f" {owner}/{name} v{version}")
|
||||
print(f" {desc[:60]}{'...' if len(desc) > 60 else ''}")
|
||||
print(f" Downloads: {downloads}")
|
||||
if tags:
|
||||
print(f" Tags: {', '.join(tags[:5])}")
|
||||
print(f" Downloads: {downloads:,}")
|
||||
print()
|
||||
|
||||
if results.total_pages > 1:
|
||||
@@ -92,6 +142,43 @@ def _cmd_registry_search(args):
|
||||
return 0
|
||||
|
||||
|
||||
def _cmd_registry_tags(args):
|
||||
"""List available tags."""
|
||||
from ..registry_client import RegistryError, get_client
|
||||
|
||||
try:
|
||||
client = get_client()
|
||||
tags = client.get_tags(
|
||||
category=getattr(args, 'category', None),
|
||||
limit=getattr(args, 'limit', 50)
|
||||
)
|
||||
|
||||
# JSON output
|
||||
if getattr(args, 'json', False):
|
||||
print(json.dumps({"tags": tags}, indent=2))
|
||||
return 0
|
||||
|
||||
if not tags:
|
||||
print("No tags found")
|
||||
return 0
|
||||
|
||||
print(f"Available tags ({len(tags)}):\n")
|
||||
for tag in tags:
|
||||
print(f" {tag['name']:20} ({tag['count']} tools)")
|
||||
|
||||
except RegistryError as e:
|
||||
if e.code == "CONNECTION_ERROR":
|
||||
print("Could not connect to the registry.", file=sys.stderr)
|
||||
else:
|
||||
print(f"Error: {e.message}", file=sys.stderr)
|
||||
return 1
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def _cmd_registry_install(args):
|
||||
"""Install a tool from the registry."""
|
||||
from ..registry_client import RegistryError
|
||||
|
||||
Reference in New Issue
Block a user