from __future__ import annotations from argparse import ArgumentParser from pathlib import Path import logging from .config import Settings from .gemini import GeminiAnalyzer from .logging_utils import configure_logging from .pipeline import ProjectPipeline logger = logging.getLogger(__name__) def build_parser() -> ArgumentParser: parser = ArgumentParser(description="Extract and explain UiPath project dependencies.") parser.add_argument("project_dir", type=Path, help="UiPath project root directory") parser.add_argument( "--output-dir", type=Path, default=Path("workspace"), help="Output bundle directory containing separate code/ and docs/ folders", ) parser.add_argument("--entry", default="main.xaml", help="Entry XAML file name") parser.add_argument("--env-file", type=Path, default=Path(".env"), help="Environment file for Gemini config") parser.add_argument("--model", help="Override GEMINI_MODEL") parser.add_argument("--skip-analysis", action="store_true", help="Skip Gemini analysis and only prepare files") parser.add_argument("--force", action="store_true", help="Overwrite the output directory if it already exists") parser.add_argument( "--log-level", default="INFO", choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], help="Logging verbosity", ) parser.add_argument("--log-file", type=Path, help="Optional file path to write logs to") return parser def main(argv: list[str] | None = None) -> int: parser = build_parser() args = parser.parse_args(argv) project_root = args.project_dir.expanduser().resolve() if not project_root.is_dir(): parser.error(f"Project directory does not exist: {project_root}") output_dir = args.output_dir.expanduser() if not output_dir.is_absolute(): output_dir = Path.cwd() / output_dir log_file = args.log_file.expanduser() if args.log_file else None if log_file is not None and not log_file.is_absolute(): log_file = Path.cwd() / log_file configure_logging(level_name=args.log_level, log_file=log_file) logger.info( "Starting CLI run: project_root=%s output_dir=%s entry=%s analysis=%s", project_root, output_dir, args.entry, not args.skip_analysis, ) env_file = args.env_file if args.env_file.exists() else None if args.env_file and env_file is None: logger.warning("Environment file not found, falling back to default dotenv lookup: %s", args.env_file) try: settings = Settings.from_env(env_file, model_override=args.model) analyzer = None if args.skip_analysis else GeminiAnalyzer(settings) pipeline = ProjectPipeline( project_root=project_root, output_root=output_dir, entry_name=args.entry, force=args.force, ) report = pipeline.run(analyzer=analyzer) except Exception: logger.exception("CLI run failed") return 1 print(f"Output written to: {report.output_root}") print(f"Code written to: {report.code_root}") print(f"Docs written to: {report.docs_root}") print(f"Final files: {len(report.final_files)}") print(f"Pruned files: {len(report.pruned_files)}") print(f"Warnings: {len(report.warnings)}") return 0