diff --git a/src/cli.py b/src/cli.py new file mode 100644 index 0000000..e517869 --- /dev/null +++ b/src/cli.py @@ -0,0 +1,75 @@ +import click +from .core.parser import parse_openapi_spec +from .core.generator import generate_docs +from .core.models import APISpec, Endpoint, RequestExample, ResponseExample +from .utils.search import search_endpoints + + +@click.group() +def main(): + """LocalAPI Docs - Generate local API documentation from OpenAPI specs.""" + pass + + +@main.command() +@click.argument('spec_file', type=click.Path(exists=True)) +@click.option('--host', '-h', default='127.0.0.1', help='Host to bind to') +@click.option('--port', '-p', default=8080, help='Port to serve on') +@click.option('--no-browser', is_flag=True, help='Don\'t open browser automatically') +def serve(spec_file, host, port, no_browser): + """Start an interactive HTML documentation server.""" + from .templates.html_template import generate_html_server + generate_html_server(spec_file, host, port, not no_browser) + + +@main.command() +@click.argument('spec_file', type=click.Path(exists=True)) +@click.option('--output', '-o', help='Output file or directory') +@click.option('--format', '-f', type=click.Choice(['html', 'markdown', 'json', 'all']), default='html', help='Output format') +@click.option('--open', is_flag=True, help='Open the generated file in browser') +def generate(spec_file, output, format, open): + """Generate static documentation in various formats.""" + generate_docs(spec_file, output, format, open) + + +@main.command() +@click.argument('spec_file', type=click.Path(exists=True)) +@click.option('--json', is_flag=True, help='Output as JSON') +def validate(spec_file, json_output): + """Validate an OpenAPI specification file.""" + result = parse_openapi_spec(spec_file) + if result.get('valid'): + click.echo("✓ OpenAPI spec is valid") + if json_output: + import json + click.echo(json.dumps(result, indent=2)) + else: + click.echo("✗ OpenAPI spec is invalid") + if json_output: + import json + click.echo(json.dumps(result, indent=2)) + else: + for error in result.get('errors', []): + click.echo(f" - {error}") + + +@main.command() +@click.argument('spec_file', type=click.Path(exists=True)) +@click.argument('query', nargs=-1) +@click.option('--limit', '-l', default=10, help='Maximum results') +@click.option('--json', is_flag=True, help='Output as JSON') +def search(spec_file, query, limit, json_output): + """Search for endpoints in an OpenAPI specification.""" + search_term = ' '.join(query) + results = search_endpoints(spec_file, search_term, limit) + if json_output: + import json + click.echo(json.dumps(results, indent=2)) + else: + if not results: + click.echo("No results found.") + return + for result in results: + click.echo(f"\n{result['method']} {result['path']}") + click.echo(f" {result.get('summary', result.get('description', 'No description'))}") + click.echo(f" Tags: {', '.join(result.get('tags', []))}")