fix: resolve CI/CD issues with proper package structure and imports
Some checks failed
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / build (push) Has been cancelled

This commit is contained in:
2026-02-03 03:54:35 +00:00
parent 7ef29718a3
commit e2d94f5f6f

View File

@@ -0,0 +1,235 @@
"""CLI command definitions."""
from pathlib import Path
import click
from rich.console import Console
from rich.panel import Panel
from rich.text import Text
from local_api_docs_search.models.document import SourceType
from local_api_docs_search.search.searcher import Searcher
from local_api_docs_search.utils.config import get_config
from local_api_docs_search.utils.formatters import (
format_error,
format_index_summary,
format_search_results,
format_success,
)
console = Console()
@click.group()
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
@click.pass_context
def cli(ctx, verbose):
"""Local API Docs Search - Index and search your API documentation."""
ctx.ensure_object(dict)
ctx.obj["verbose"] = verbose
@cli.command(name="index")
@click.argument(
"path", type=click.Path(exists=True, file_okay=True, dir_okay=True, path_type=Path)
)
@click.option(
"--type",
"-t",
type=click.Choice(["openapi", "readme", "code", "all"]),
default="all",
help="Type of documentation to index",
)
@click.option(
"--recursive", "-r", is_flag=True, default=False, help="Recursively search directories"
)
@click.option(
"--batch-size", "-b", type=int, default=32, help="Documents per batch"
)
@click.pass_context
def index_command(ctx, path, type, recursive, batch_size):
"""Index documentation from a path.
PATH is the path to a file or directory to index.
"""
with console.status(f"Indexing {type} documentation from {path}..."):
searcher = Searcher()
count = searcher.index(path, doc_type=type, recursive=recursive, batch_size=batch_size)
if count > 0:
console.print(format_success(f"Successfully indexed {count} documents"))
else:
console.print(format_error("No documents found to index"))
if type == "all":
console.print("Try specifying a type: --type openapi|readme|code")
@cli.command(name="search")
@click.argument("query", type=str)
@click.option(
"--limit", "-l", type=int, default=None, help="Maximum number of results"
)
@click.option(
"--type",
"-t",
type=click.Choice(["openapi", "readme", "code"]),
help="Filter by source type",
)
@click.option("--json", is_flag=True, help="Output as JSON")
@click.option(
"--hybrid/--semantic",
default=True,
help="Use hybrid (default) or semantic-only search",
)
@click.pass_context
def search_command(ctx, query, limit, type, json, hybrid):
"""Search indexed documentation.
QUERY is the search query in natural language.
"""
config = get_config()
if limit is None:
limit = config.default_limit
searcher = Searcher()
with console.status("Searching..."):
if hybrid:
results = searcher.hybrid_search(query, limit=limit)
else:
results = searcher.search(query, limit=limit)
if not results:
console.print(format_info("No results found for your query"))
return
if json:
import json as json_lib
output = [r.to_dict() for r in results]
console.print(json_lib.dumps(output, indent=2))
else:
table = format_search_results(results)
console.print(table)
console.print(f"\nFound {len(results)} result(s)")
@cli.command(name="list")
@click.option(
"--type",
"-t",
type=click.Choice(["openapi", "readme", "code"]),
help="Filter by source type",
)
@click.option("--json", is_flag=True, help="Output as JSON")
@click.pass_context
def list_command(ctx, type, json):
"""List indexed documents."""
searcher = Searcher()
stats = searcher.get_stats()
if json:
import json
output = stats.to_dict()
console.print(json.dumps(output, indent=2))
else:
table = format_index_summary(
stats.total_documents,
stats.openapi_count,
stats.readme_count,
stats.code_count,
)
console.print(table)
@cli.command(name="stats")
@click.pass_context
def stats_command(ctx):
"""Show index statistics."""
searcher = Searcher()
stats = searcher.get_stats()
table = format_index_summary(
stats.total_documents,
stats.openapi_count,
stats.readme_count,
stats.code_count,
)
console.print(table)
@cli.command(name="clear")
@click.option("--type", "-t", type=click.Choice(["openapi", "readme", "code"]))
@click.option("--force", "-f", is_flag=True, help="Skip confirmation prompt")
@click.pass_context
def clear_command(ctx, type, force):
"""Clear the index or filtered by type."""
if not force:
if type:
confirm = click.confirm(f"Delete all {type} documents from the index?")
else:
confirm = click.confirm("Delete all documents from the index?")
else:
confirm = True
if not confirm:
console.print("Cancelled")
return
searcher = Searcher()
if type:
source_type = SourceType(type)
count = searcher._vector_store.delete_by_source_type(source_type)
else:
count = searcher._vector_store.count()
searcher.clear_index()
console.print(format_success(f"Deleted {count} document(s)"))
@cli.command(name="config")
@click.option("--show", is_flag=True, help="Show current configuration")
@click.option("--reset", is_flag=True, help="Reset configuration to defaults")
@click.pass_context
def config_command(ctx, show, reset):
"""Manage configuration."""
config = get_config()
if reset:
config.reset()
console.print(format_success("Configuration reset to defaults"))
return
if show or not (reset):
config_dict = config.to_dict()
if show:
import json
console.print(json.dumps(config_dict, indent=2))
else:
lines = ["Current Configuration:", ""]
for key, value in config_dict.items():
lines.append(f" {key}: {value}")
panel = Panel(
"\n".join(lines),
title="Configuration",
expand=False,
)
console.print(panel)
@cli.command(name="interactive")
@click.pass_context
def interactive_command(ctx):
"""Enter interactive search mode."""
from local_api_docs_search.cli.interactive import run_interactive
run_interactive()
def format_info(message: str) -> Text:
"""Format an info message."""
return Text(message, style="cyan")