"""CLI: python -m behaviour {package DIR | diff FILE|- | snippet FILE|-} [--task TEXT | --task-file F] [--format text|json|both] [--lang js|ts|py|sh] [--out-json PATH] Static only: analysed code is never executed. Output describes behaviour; it is not a verdict. """ import argparse import json import sys from pathlib import Path from .engine import analyse_diff, analyse_package, analyse_snippet from .model import KIND_HELP def _read(src): return sys.stdin.read() if src == "-" else Path(src).read_text(encoding="utf-8", errors="replace") def main(argv=None): ap = argparse.ArgumentParser(prog="python -m behaviour", description=__doc__.split("\n\n")[0]) sub = ap.add_subparsers(dest="mode") for name in ("package", "diff", "snippet"): p = sub.add_parser(name) p.add_argument("input", help="directory" if name == "package" else "file or - for stdin") p.add_argument("--task") p.add_argument("--task-file") p.add_argument("--format", choices=("text", "json", "both"), default="text") p.add_argument("--out-json") if name == "package": p.add_argument("--include-node-modules", action="store_true") if name == "snippet": p.add_argument("--lang", choices=("js", "ts", "py", "sh", "javascript", "typescript", "python", "shell")) sub.add_parser("kinds") a = ap.parse_args(argv) if a.mode == "kinds": for k, v in KIND_HELP.items(): print(f"{k}: {v}") return 0 if not a.mode: ap.print_help() return 2 task = a.task or (Path(a.task_file).read_text(errors="replace") if a.task_file else None) if a.mode == "package": rep = analyse_package(a.input, task=task, include_node_modules=a.include_node_modules) elif a.mode == "diff": rep = analyse_diff(_read(a.input), task=task, target=a.input) else: rep = analyse_snippet(_read(a.input), lang=a.lang, task=task, target="snippet" if a.input == "-" else a.input) if a.out_json: Path(a.out_json).write_text(json.dumps(rep, indent=1) + "\n") if a.format in ("json", "both"): print(json.dumps(rep, indent=1)) if a.format in ("text", "both"): print(rep["text"]) return 0 if __name__ == "__main__": sys.exit(main())