diff --git a/README.md b/README.md index e94ba2c..8de9272 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ python -m logpilot.cli analyze [OPTIONS] ### Options +- `--version` : Show the current logpilot version and exit - `--format [auto|json|text]` : Log format (default: auto) - `--output [text|json]` : Output type (default: text) - `--max-tokens ` : Max tokens per chunk (default: 2048) diff --git a/logpilot/cli.py b/logpilot/cli.py index 4a57422..8a52267 100644 --- a/logpilot/cli.py +++ b/logpilot/cli.py @@ -1,11 +1,40 @@ +import os + import typer +try: + import tomllib +except ImportError: + import toml as tomllib + from logpilot.chunker import chunk_logs from logpilot.llm_client import LLMClient from logpilot.log_parser import parse_logs from logpilot.postprocessor import aggregate_responses from logpilot.prompt_engine import format_prompt + +def get_version() -> str: + pyproject_path = os.path.join( + os.path.dirname(os.path.dirname(__file__)), "pyproject.toml" + ) + try: + with open(pyproject_path, "rb") as f: + data = tomllib.load(f) + version = data.get("project", {}).get("version") + if version: + return version + except Exception: + pass + return "unknown" + + +def version_callback(value: bool): + if value: + typer.echo(f"logpilot version {get_version()}") + raise typer.Exit() + + app = typer.Typer() @@ -70,5 +99,19 @@ def analyze( typer.echo(summary) +@app.callback() +def main( + version: bool = typer.Option( + None, + "--version", + help="Show the version and exit.", + callback=version_callback, + is_eager=True, + ), +): + """Logpilot CLI""" + pass + + if __name__ == "__main__": app() diff --git a/pyproject.toml b/pyproject.toml index 07bc427..b277ba0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,6 @@ +[project] +name = "logpilot" +version = "0.1.0" [tool.black] line-length = 88 skip-string-normalization = false diff --git a/requirements.txt b/requirements.txt index 0f16ce6..320160d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +toml typer rich git+https://github.com/github/copilot-sdk.git#subdirectory=python diff --git a/tests/test_cli.py b/tests/test_cli.py index 7b7f0cd..2707ab0 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -13,7 +13,7 @@ def run_cli(args, env=None): def test_cli_missing_file(): - result = run_cli(["not_a_real_file.log"]) + result = run_cli(["analyze", "not_a_real_file.log"]) assert result.returncode != 0 assert "No such file" in result.stderr or "Error" in result.stderr @@ -21,7 +21,7 @@ def test_cli_missing_file(): def test_cli_empty_file(tmp_path): log_file = tmp_path / "empty.log" log_file.write_text("") - result = run_cli([str(log_file)]) + result = run_cli(["analyze", str(log_file)]) assert result.returncode != 0 assert "No log entries found" in result.stderr or "Error" in result.stderr @@ -29,7 +29,7 @@ def test_cli_empty_file(tmp_path): def test_cli_invalid_format(tmp_path): log_file = tmp_path / "invalid.log" log_file.write_text("not a log line\nnot json\n") - result = run_cli([str(log_file)]) + result = run_cli(["analyze", str(log_file)]) assert result.returncode != 0 assert "No log entries found" in result.stderr or "Error" in result.stderr @@ -44,7 +44,7 @@ def test_cli_out_file(tmp_path): ) ) out_file = tmp_path / "out.txt" - result = run_cli([str(log_file), "--out-file", str(out_file)]) + result = run_cli(["analyze", str(log_file), "--out-file", str(out_file)]) assert result.returncode == 0, f"stdout: {result.stdout}\nstderr: {result.stderr}" assert out_file.read_text().strip() != "" @@ -58,7 +58,7 @@ def test_cli_output_json(tmp_path): '"message": "Something failed"}\n' ) ) - result = run_cli([str(log_file), "--output", "json"]) + result = run_cli(["analyze", str(log_file), "--output", "json"]) assert result.returncode == 0, f"stdout: {result.stdout}\nstderr: {result.stderr}" import json as pyjson @@ -78,14 +78,14 @@ def test_cli_format_json(tmp_path): '"message": "Something failed"}\n' ) ) - result = run_cli([str(log_file), "--format", "json"]) + result = run_cli(["analyze", str(log_file), "--format", "json"]) assert result.returncode == 0, f"stdout: {result.stdout}\nstderr: {result.stderr}" def test_cli_format_text(tmp_path): log_file = tmp_path / "test.log" log_file.write_text("plain log line\n") - result = run_cli([str(log_file), "--format", "text"]) + result = run_cli(["analyze", str(log_file), "--format", "text"]) assert result.returncode != 0 or "plain log line" in result.stdout @@ -94,7 +94,7 @@ def test_cli_unreadable_file(tmp_path): log_file.write_text("test") log_file.chmod(0) try: - result = run_cli([str(log_file)]) + result = run_cli(["analyze", str(log_file)]) assert result.returncode != 0 # Accept either error message or 'No log entries found.' valid = ( @@ -117,7 +117,7 @@ def test_cli_large_file(tmp_path): ) * 10000 ) - result = run_cli([str(log_file)]) + result = run_cli(["analyze", str(log_file)]) assert result.returncode == 0, f"stdout: {result.stdout}\nstderr: {result.stderr}" @@ -160,7 +160,7 @@ def test_cli_analyze_basic(tmp_path, monkeypatch): env = dict(os.environ) env["LOGPILOT_MOCK_LLM"] = "1" result = subprocess.run( - [sys.executable, "-m", "logpilot.cli", str(log_file)], + [sys.executable, "-m", "logpilot.cli", "analyze", str(log_file)], capture_output=True, text=True, env=env,