"""CLI entry point for taste-engine.""" import argparse import sys from pathlib import Path def main(): parser = argparse.ArgumentParser(prog="taste", description="Personal visual taste engine") sub = parser.add_subparsers(dest="command") # taste scan scan_p = sub.add_parser("scan", help="Run full scan pipeline") scan_p.add_argument("--config", type=Path, help="Config YAML path") scan_p.add_argument("--profile", default="default", help="User profile name") scan_p.add_argument("--stores", help="Comma-separated store names to scrape") scan_p.add_argument("--output", type=Path, help="Output HTML path") # taste stats stats_p = sub.add_parser("stats", help="Show DB stats") stats_p.add_argument("--config", type=Path, help="Config YAML path") stats_p.add_argument("--profile", default="default", help="User profile name") # taste profile profile_p = sub.add_parser("profile", help="Manage user profiles") profile_p.add_argument("--config", type=Path, help="Config YAML path") profile_p.add_argument("--name", default="default", help="Profile name") profile_p.add_argument("--output", type=Path, help="Output HTML path") profile_sub = profile_p.add_subparsers(dest="profile_command") profile_sub.add_parser("list", help="List profiles") create_p = profile_sub.add_parser("create", help="Create a new profile") create_p.add_argument("profile_name", help="Profile name") profile_sub.add_parser("page", help="Generate profile management page") args = parser.parse_args() if args.command == "scan": _cmd_scan(args) elif args.command == "stats": _cmd_stats(args) elif args.command == "profile": _cmd_profile(args) else: parser.print_help() def _cmd_scan(args): from taste.config import load_config, load_profile from taste.pipeline import run_scan config = load_config(args.config) # Filter stores if --stores specified if args.stores: enabled = set(s.strip().lower() for s in args.stores.split(",")) for name, sc in config.stores.items(): sc.enabled = name.lower() in enabled profile = load_profile(args.profile, config) run_scan(config, profile, output_path=args.output) def _cmd_stats(args): from taste.config import load_config, load_profile from taste.store import ProductStore config = load_config(args.config) profile = load_profile(args.profile, config) store = ProductStore(db_path=profile.db_path(config)) stats = store.get_stats() print(f"Total products: {stats['total']}") print() for name, s in stats["stores"].items(): print(f" {name:15s} {s['count']:5d} products top={s['top']:.3f} avg={s['avg']:.3f}") def _cmd_profile(args): from taste.config import load_config, load_profile config = load_config(getattr(args, "config", None)) if args.profile_command == "list": profiles_dir = config.data_dir / "profiles" if not profiles_dir.exists(): print("No profiles found. Create one with: taste profile create ") return for f in sorted(profiles_dir.iterdir()): if f.suffix in (".yaml", ".json"): print(f" {f.stem}") elif args.profile_command == "create": profiles_dir = config.data_dir / "profiles" profiles_dir.mkdir(parents=True, exist_ok=True) name = args.profile_name path = profiles_dir / f"{name}.yaml" if path.exists(): print(f"Profile '{name}' already exists at {path}") return template = f"""name: {name} reference_dirs: [] liked_urls: [] disliked_brands: [] filters: {{}} style_text_queries: [] """ path.write_text(template) print(f"Created profile: {path}") print(f"Edit it to add your preferences, then run: taste scan --profile {name}") elif args.profile_command == "page": from taste.profile_page import generate_profile_page profile = load_profile(args.name, config) output = args.output or Path.cwd() / "profile.html" generate_profile_page(config, profile, output) print(f"open {output}") else: print("Usage: taste profile [list|create |page]") if __name__ == "__main__": main()