#!/usr/bin/env python3 """ GURMA.ai Research Tool — CLI entry point. Usage: python research.py search "rehabilitation robotics market" python research.py batch python research.py competitor "Ekso Bionics" python research.py competitor --list-categories python research.py extract python research.py list python research.py sota python research.py sota --analyze notes/research/podcast.md python research.py mali python research.py fonlar -c tubitak """ from __future__ import annotations import argparse import sys try: from .config import RESEARCH_DIR, COMPETITORS, BATCH_QUERY_TEMPLATES, MARKET_QUERIES, LLM_ENABLED from .search import SearchService, ResultStorage from .extract import CompetitorExtractor from .intel import CompetitorIntelAgent, DEEP_INTEL_CATEGORIES except ImportError: from config import RESEARCH_DIR, COMPETITORS, BATCH_QUERY_TEMPLATES, MARKET_QUERIES, LLM_ENABLED from search import SearchService, ResultStorage from extract import CompetitorExtractor from intel import CompetitorIntelAgent, DEEP_INTEL_CATEGORIES # ============================================================ # Commands # ============================================================ def cmd_search(args): service = SearchService(backend=args.backend) print(f"Searching: {args.query}") print(f"Backend: {args.backend} | Max: {args.max_results}") print("-" * 50) results = service.search(args.query, args.max_results, save=args.save) for i, r in enumerate(results, 1): print(f"\n{i}. {r.title}") print(f" {r.url}") print(f" {r.snippet[:150]}...") print(f"\n[{len(results)} results]") if args.save: print(f"Saved to: {RESEARCH_DIR}") def cmd_batch(args): service = SearchService(backend=args.backend) storage = ResultStorage() queries = [] for company in COMPETITORS: for template in BATCH_QUERY_TEMPLATES: queries.append(template.format(company=company)) queries.extend(MARKET_QUERIES) total_queries = len(queries) skipped = 0 if not args.force: recent = storage.get_recent_queries(days=args.days) original_count = len(queries) queries = [q for q in queries if q.lower().strip() not in recent] skipped = original_count - len(queries) print(f"Batch Research") print(f"{'='*60}") print(f"Competitors: {len(COMPETITORS)}") print(f"Total queries: {total_queries}") if skipped > 0: print(f"Skipped (run in last {args.days} days): {skipped}") print(f"New queries to run: {len(queries)}") print(f"Output: {RESEARCH_DIR}") print(f"{'='*60}") if not queries: print("\nNo new queries to run. Use --force to re-run all.") return def progress(i, total, query): print(f"\n[{i}/{total}] {query}") stats = service.search_batch(queries, args.max_results, args.delay, callback=progress) success = sum(1 for v in stats.values() if v >= 0) print(f"\n{'='*60}") print(f"Complete: {success}/{len(queries)} successful") if skipped > 0: print(f"Skipped: {skipped} (already run recently)") print(f"{'='*60}") def cmd_competitor(args): company = args.company use_external_llm = args.external_llm if use_external_llm and not LLM_ENABLED: print("Warning: --external-llm requested but OPENROUTER_API_KEY not found. Skipping external LLM.") use_external_llm = False categories = None if args.categories: categories = [c.strip() for c in args.categories.split(",")] valid = set(DEEP_INTEL_CATEGORIES.keys()) invalid = [c for c in categories if c not in valid] if invalid: print(f"Invalid categories: {invalid}") print(f"Valid: {sorted(valid)}") return if args.list_categories: print("Available categories:") for key, cat in DEEP_INTEL_CATEGORIES.items(): q_count = len(cat["queries"]) print(f" {key:30s} {cat['label']:30s} ({q_count} queries)") return agent = CompetitorIntelAgent(company) report_path = agent.run( categories=categories, use_external_llm=use_external_llm, delay=args.delay, max_results=args.max_results, ) print(f"\nReport: {report_path}") def cmd_extract(args): extractor = CompetitorExtractor() print(f"Loading research from: {extractor.research_dir}") data = extractor.process() if not data["competitors"]: print("No research files found. Run 'batch' first.") return output = extractor.save(data) print(f"Saved to: {output}") print(f"\nCompany mentions:") for comp in data["competitors"]: status_marker = {"collapsed": "⚠", "weak": "↓", "growing": "↑", "strong": "★"}.get(comp["status"], "•") print(f" {status_marker} {comp['name']}: {comp['mentions']} mentions ({comp['status']})") def cmd_sota(args): try: from .sota_agent import SOTAScoutAgent except ImportError: from sota_agent import SOTAScoutAgent agent = SOTAScoutAgent() if args.analyze: report = agent.analyze(args.analyze) print(f"\nAnalysis report: {report}") return agent.show(section=args.show) def cmd_mali(args): try: from .tr_agents import MaliMusavirAgent except ImportError: from tr_agents import MaliMusavirAgent agent = MaliMusavirAgent() if args.list_categories: agent.list_categories() return categories = None if args.categories: categories = [c.strip() for c in args.categories.split(",")] valid = set(agent.CATEGORIES.keys()) invalid = [c for c in categories if c not in valid] if invalid: print(f"Geçersiz kategoriler: {invalid}") print(f"Geçerli: {sorted(valid)}") return report_path = agent.run( categories=categories, delay=args.delay, max_results=args.max_results, ) print(f"\nRapor: {report_path}") def cmd_fonlar(args): try: from .tr_agents import FonArastirmaAgent except ImportError: from tr_agents import FonArastirmaAgent agent = FonArastirmaAgent() if args.list_categories: agent.list_categories() return categories = None if args.categories: categories = [c.strip() for c in args.categories.split(",")] valid = set(agent.CATEGORIES.keys()) invalid = [c for c in categories if c not in valid] if invalid: print(f"Geçersiz kategoriler: {invalid}") print(f"Geçerli: {sorted(valid)}") return report_path = agent.run( categories=categories, delay=args.delay, max_results=args.max_results, ) print(f"\nRapor: {report_path}") def cmd_list(args): storage = ResultStorage() searches = storage.list_searches(args.limit) if not searches: print(f"No searches in {RESEARCH_DIR}") return print(f"Recent searches ({RESEARCH_DIR}):\n") for s in searches: print(f" {s['timestamp'][:10]} {s['results']:2d} results {s['query'][:50]}") # ============================================================ # Argparse # ============================================================ def main(): parser = argparse.ArgumentParser( description="GURMA.ai Research Tool", formatter_class=argparse.RawDescriptionHelpFormatter ) subparsers = parser.add_subparsers(dest="command", help="Commands") # search p_search = subparsers.add_parser("search", help="Single web search") p_search.add_argument("query", help="Search query") p_search.add_argument("-b", "--backend", default="duckduckgo", choices=["duckduckgo", "ddg", "serpapi", "brave"]) p_search.add_argument("-n", "--max-results", type=int, default=10) p_search.add_argument("--no-save", dest="save", action="store_false") p_search.set_defaults(func=cmd_search) # batch p_batch = subparsers.add_parser("batch", help="Batch research all competitors") p_batch.add_argument("-b", "--backend", default="duckduckgo") p_batch.add_argument("-n", "--max-results", type=int, default=10) p_batch.add_argument("-d", "--delay", type=float, default=0.5) p_batch.add_argument("--days", type=int, default=7, help="Skip queries run within N days (default: 7)") p_batch.add_argument("-f", "--force", action="store_true", help="Force re-run all queries (ignore deduplication)") p_batch.set_defaults(func=cmd_batch) # competitor (deep intel) p_comp = subparsers.add_parser("competitor", help="Deep competitive intelligence on a company") p_comp.add_argument("company", nargs="?", default="", help="Company name (e.g. 'Ekso Bionics')") p_comp.add_argument("--external-llm", action="store_true", help="Also use external LLM (OpenRouter) for enhanced analysis") p_comp.add_argument("-c", "--categories", type=str, default=None, help="Comma-separated categories (default: all)") p_comp.add_argument("--list-categories", action="store_true", help="List available categories") p_comp.add_argument("-n", "--max-results", type=int, default=10) p_comp.add_argument("-d", "--delay", type=float, default=1.0, help="Delay between searches in seconds (default: 1.0)") p_comp.set_defaults(func=cmd_competitor) # sota p_sota = subparsers.add_parser("sota", help="SOTA technology knowledge base for GURMA.ai") p_sota.add_argument("--analyze", "-a", type=str, default=None, help="Analyze a document and update knowledge base") p_sota.add_argument("--show", "-s", type=str, default=None, nargs="?", const=None, choices=["models", "techniques", "stack", "principles", "actions", "sources"], help="Show specific KB section (default: summary)") p_sota.set_defaults(func=cmd_sota) # mali (Turkish company formation) p_mali = subparsers.add_parser("mali", help="Türkiye şirket kuruluşu araştırması") p_mali.add_argument("-c", "--categories", type=str, default=None, help="Virgülle ayrılmış kategoriler (varsayılan: tümü)") p_mali.add_argument("--list-categories", action="store_true", help="Mevcut kategorileri listele") p_mali.add_argument("-n", "--max-results", type=int, default=10) p_mali.add_argument("-d", "--delay", type=float, default=1.0) p_mali.set_defaults(func=cmd_mali) # fonlar (Turkish government funding research) p_fonlar = subparsers.add_parser("fonlar", help="TÜBİTAK ve devlet fonları araştırması") p_fonlar.add_argument("-c", "--categories", type=str, default=None, help="Virgülle ayrılmış kategoriler (varsayılan: tümü)") p_fonlar.add_argument("--list-categories", action="store_true", help="Mevcut kategorileri listele") p_fonlar.add_argument("-n", "--max-results", type=int, default=10) p_fonlar.add_argument("-d", "--delay", type=float, default=1.0) p_fonlar.set_defaults(func=cmd_fonlar) # extract p_extract = subparsers.add_parser("extract", help="Extract competitor data to JSON") p_extract.set_defaults(func=cmd_extract) # list p_list = subparsers.add_parser("list", help="List saved searches") p_list.add_argument("-l", "--limit", type=int, default=20) p_list.set_defaults(func=cmd_list) args = parser.parse_args() if hasattr(args, "func"): args.func(args) else: parser.print_help() if __name__ == "__main__": main()