Files
q3alique 3fe6011162 Rename --llm flag to --review and review_exporter
- cli.py: --llm -> --review, param and output key updated
- orchestrator.py: generate_llm -> generate_review, output .llm.md ->
  .review.md, import llm_exporter -> review_exporter
- llm_exporter.py renamed to review_exporter.py; internal header and
  section titles updated to remove tool-name references
- .gitignore: report.llm.md / *.llm.md -> report.review.md / *.review.md
- README.md, demo/README.md: all --llm flags and report.llm.md references
  updated to --review and report.review.md
2026-06-08 01:43:37 +02:00

86 lines
3.3 KiB
Python

from __future__ import annotations
import click
import traceback
import sys
from pathlib import Path
@click.group()
def cli():
"""codeflow — static analysis and visualization for any code repository."""
pass
@cli.command("analyze")
@click.argument("repo_path", type=click.Path(
exists=True, file_okay=False, dir_okay=True, readable=True, path_type=str,
))
@click.option("--output", "-o", required=True, type=click.Path(path_type=str),
help="Path for the output HTML report (e.g., report.html)")
@click.option("--cluster-threshold", default=5, show_default=True,
type=click.IntRange(0, 100),
help="Number of sibling nodes above which clustering is triggered. 0 disables clustering.")
@click.option("--max-file-size", default=5, show_default=True, type=float,
help="Skip files larger than this many megabytes.")
@click.option("--burp", is_flag=True, default=False,
help="Generate a Burp Suite JSON export file alongside the report.")
@click.option("--review", is_flag=True, default=False,
help="Generate a Markdown taint review document alongside the report (report.review.md).")
@click.option("--verbose", "-v", is_flag=True, default=False,
help="Print verbose progress to stderr.")
def analyze(repo_path, output, cluster_threshold, max_file_size, burp, review, verbose):
"""Analyze a repository and produce an HTML report."""
output_path = Path(output)
parent = output_path.parent
if not parent.exists():
click.echo(f"Error: output directory does not exist: {parent}", err=True)
sys.exit(1)
if not output.lower().endswith(".html"):
click.echo("Error: output must be a .html file", err=True)
sys.exit(1)
click.echo(f"[codeflow] Analyzing {repo_path} ...", err=True)
try:
from codeflow.orchestrator import run_analysis
result = run_analysis(
repo_path=Path(repo_path).resolve(),
output_path=output_path.resolve(),
cluster_threshold=cluster_threshold,
max_file_size_mb=max_file_size,
generate_burp=burp,
generate_review=review,
verbose=verbose,
)
except Exception as e:
click.echo(f"[codeflow] FATAL ERROR: {type(e).__name__}: {e}", err=True)
if verbose:
traceback.print_exc()
sys.exit(2)
html_path = result.get("html_path", "")
burp_path = result.get("burp_path")
file_count = result.get("file_count", 0)
node_count = result.get("node_count", 0)
flow_count = result.get("flow_count", 0)
elapsed = result.get("elapsed_seconds", 0.0)
errors = result.get("errors", [])
review_path = result.get("review_path")
click.echo("[codeflow] Done.")
click.echo(f" Report: {html_path}")
if burp_path:
click.echo(f" Burp export: {burp_path}")
if review_path:
click.echo(f" Review doc: {review_path}")
click.echo(f" Files: {file_count}")
click.echo(f" Nodes: {node_count}")
click.echo(f" Flows: {flow_count}")
click.echo(f" Time: {elapsed:.1f}s")
if errors:
click.echo(f"[codeflow] WARNING: {len(errors)} file(s) had parse errors:", err=True)
for err in errors:
click.echo(f" - {err}", err=True)