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:
rob
2026-01-13 06:13:37 -04:00
co-authored by Claude Opus 4.5
parent 614b145f5f
commit 14448408af
8 changed files with 762 additions and 101 deletions
+91 -4
View File
@@ -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