Search for integrations in the active catalog stack.
(
query: Optional[str] = typer.Argument(None, help="Search query (optional)"),
tag: Optional[str] = typer.Option(None, "--tag", help="Filter by tag"),
author: Optional[str] = typer.Option(None, "--author", help="Filter by author"),
)
| 262 | # not additive like extensions and presets. |
| 263 | @integration_app.command("search") |
| 264 | def integration_search( |
| 265 | query: Optional[str] = typer.Argument(None, help="Search query (optional)"), |
| 266 | tag: Optional[str] = typer.Option(None, "--tag", help="Filter by tag"), |
| 267 | author: Optional[str] = typer.Option(None, "--author", help="Filter by author"), |
| 268 | ): |
| 269 | """Search for integrations in the active catalog stack.""" |
| 270 | from . import INTEGRATION_REGISTRY |
| 271 | from .catalog import ( |
| 272 | IntegrationCatalog, |
| 273 | IntegrationCatalogError, |
| 274 | IntegrationValidationError, |
| 275 | ) |
| 276 | from .. import _require_specify_project |
| 277 | |
| 278 | project_root = _require_specify_project() |
| 279 | integration_config = _read_integration_json(project_root) |
| 280 | installed_key = _default_integration_key(integration_config) |
| 281 | catalog = IntegrationCatalog(project_root) |
| 282 | |
| 283 | try: |
| 284 | results = catalog.search(query=query, tag=tag, author=author) |
| 285 | except IntegrationValidationError as exc: |
| 286 | console.print(f"[red]Error:[/red] {exc}") |
| 287 | console.print( |
| 288 | "\nTip: Check the configuration file path shown above for invalid catalog configuration " |
| 289 | "(for example, .specify/integration-catalogs.yml or ~/.specify/integration-catalogs.yml)." |
| 290 | ) |
| 291 | raise typer.Exit(1) |
| 292 | except IntegrationCatalogError as exc: |
| 293 | console.print(f"[red]Error:[/red] {exc}") |
| 294 | if os.environ.get("SPECKIT_INTEGRATION_CATALOG_URL", "").strip(): |
| 295 | console.print( |
| 296 | "\nTip: Check the SPECKIT_INTEGRATION_CATALOG_URL environment variable for an invalid " |
| 297 | "catalog URL, or unset it to use the configured catalog files " |
| 298 | "(.specify/integration-catalogs.yml or ~/.specify/integration-catalogs.yml)." |
| 299 | ) |
| 300 | else: |
| 301 | console.print("\nTip: The catalog may be temporarily unavailable. Try again later.") |
| 302 | raise typer.Exit(1) |
| 303 | |
| 304 | if not results: |
| 305 | console.print("\n[yellow]No integrations found matching criteria[/yellow]") |
| 306 | if query or tag or author: |
| 307 | console.print("\nTry:") |
| 308 | console.print(" • Broader search terms") |
| 309 | console.print(" • Remove filters") |
| 310 | console.print(" • specify integration search (show all)") |
| 311 | return |
| 312 | |
| 313 | console.print(f"\n[green]Found {len(results)} integration(s):[/green]\n") |
| 314 | for integ in sorted(results, key=lambda e: e.get("id", "")): |
| 315 | iid = integ.get("id", "?") |
| 316 | name = integ.get("name", iid) |
| 317 | version = integ.get("version", "?") |
| 318 | console.print(f"[bold]{name}[/bold] ({iid}) v{version}") |
| 319 | desc = integ.get("description", "") |
| 320 | if desc: |
| 321 | console.print(f" {desc}") |
nothing calls this directly
no test coverage detected