#!/usr/bin/env python3 import argparse import json import sys from datetime import datetime, UTC from intel_core import ( analyze_events, validate_analyze_payload, validate_collect_payload, validate_run_payload, ) from search_provider import ( InvalidSearchInputError, MissingAPIKeyError, SearchProviderError, UnsupportedProviderError, UpstreamHTTPError, collect_events, ) ERROR_TEMPLATE = { "success": False, "code": "invalid_input", "message": "", "data": {}, "meta": {}, "errors": [], } def _now_iso() -> str: return datetime.now(UTC).isoformat() def _emit(data: dict, pretty: bool, stream=None): print(json.dumps(data, ensure_ascii=False, indent=2 if pretty else None), file=stream or sys.stdout) def _error_response(code: str, message: str, errors: list[str] | None = None) -> dict: return { **ERROR_TEMPLATE, "code": code, "message": message, "meta": {"generated_at": _now_iso()}, "errors": errors or [], } def _load_payload(raw: str) -> dict: return json.loads(raw) def _override_search(payload: dict, args: argparse.Namespace) -> dict: payload.setdefault("data", {}) payload["data"].setdefault("search", {}) if getattr(args, "provider", None): payload["data"]["search"]["provider"] = args.provider if getattr(args, "freshness", None): payload["data"]["search"]["freshness"] = args.freshness if getattr(args, "count", None) is not None: payload["data"]["search"]["count"] = args.count if getattr(args, "max_events", None) is not None: payload["max_events"] = args.max_events return payload def cmd_collect(args: argparse.Namespace): payload = _override_search(_load_payload(args.input_json), args) errors = validate_collect_payload(payload) if errors: _emit(_error_response("invalid_input", "payload invalid", errors), args.pretty, sys.stderr) raise SystemExit(1) events = collect_events(payload) result = { "success": True, "code": "ok", "message": "events collected", "data": {"developments": events}, "meta": {"generated_at": _now_iso(), "source_count": len(events)}, "errors": [], } _emit(result, args.pretty) def cmd_analyze(args: argparse.Namespace): payload = _override_search(_load_payload(args.input_json), args) errors = validate_analyze_payload(payload) if errors: _emit(_error_response("invalid_input", "payload invalid", errors), args.pretty, sys.stderr) raise SystemExit(1) events = payload.get("data", {}).get("developments", []) data = analyze_events(payload, events) result = { "success": True, "code": "ok", "message": "intelligence analyzed", "data": data, "meta": { "generated_at": _now_iso(), "raw_count": data.get("stats", {}).get("raw_count", len(events)), "dedup_count": data.get("stats", {}).get("dedup_count", len(events)), "returned_count": data.get("stats", {}).get("returned_count", len(data.get("developments", []))), }, "errors": [], } if args.output == "markdown": print(data["markdown"]) return _emit(result, args.pretty) def cmd_run(args: argparse.Namespace): payload = _override_search(_load_payload(args.input_json), args) errors = validate_run_payload(payload) if errors: _emit(_error_response("invalid_input", "payload invalid", errors), args.pretty, sys.stderr) raise SystemExit(1) events = collect_events(payload) data = analyze_events(payload, events) result = { "success": True, "code": "ok", "message": "intelligence brief generated", "data": data, "meta": { "generated_at": _now_iso(), "raw_count": data.get("stats", {}).get("raw_count", len(events)), "dedup_count": data.get("stats", {}).get("dedup_count", len(events)), "returned_count": data.get("stats", {}).get("returned_count", len(data.get("developments", []))), }, "errors": [], } if args.output == "markdown": print(data["markdown"]) return _emit(result, args.pretty) def cmd_plan_recurring(args: argparse.Namespace): payload = _override_search(_load_payload(args.input_json), args) errors = validate_run_payload(payload) if errors: _emit(_error_response("invalid_input", "payload invalid", errors), args.pretty, sys.stderr) raise SystemExit(1) data = analyze_events(payload, []) result = { "success": True, "code": "ok", "message": "recurring plan generated", "data": {"schedule_payload": data["schedule_payload"]}, "meta": {"generated_at": _now_iso()}, "errors": [], } _emit(result, args.pretty) def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description="Generate competitor intelligence") subparsers = parser.add_subparsers(dest="command", required=True) for name in ["collect", "analyze", "run", "plan-recurring"]: sub = subparsers.add_parser(name) sub.add_argument("--input-json", required=True) sub.add_argument("--provider") sub.add_argument("--freshness") sub.add_argument("--count", type=int) sub.add_argument("--max-events", type=int) sub.add_argument("--pretty", action="store_true") if name in {"analyze", "run"}: sub.add_argument("--output", choices=["json", "markdown"], default="json") subparsers.choices["collect"].set_defaults(func=cmd_collect) subparsers.choices["analyze"].set_defaults(func=cmd_analyze) subparsers.choices["run"].set_defaults(func=cmd_run) subparsers.choices["plan-recurring"].set_defaults(func=cmd_plan_recurring) return parser if __name__ == "__main__": parser = build_parser() args = parser.parse_args() try: args.func(args) except json.JSONDecodeError as exc: _emit(_error_response("invalid_input", f"invalid json: {exc}", [str(exc)]), getattr(args, "pretty", False), sys.stderr) raise SystemExit(1) except MissingAPIKeyError as exc: _emit(_error_response("missing_api_key", str(exc), [str(exc)]), getattr(args, "pretty", False), sys.stderr) raise SystemExit(1) except UnsupportedProviderError as exc: _emit(_error_response("unsupported_provider", str(exc), [str(exc)]), getattr(args, "pretty", False), sys.stderr) raise SystemExit(1) except InvalidSearchInputError as exc: _emit(_error_response("invalid_input", str(exc), [str(exc)]), getattr(args, "pretty", False), sys.stderr) raise SystemExit(1) except UpstreamHTTPError as exc: _emit(_error_response("upstream_http_error", str(exc), [str(exc)]), getattr(args, "pretty", False), sys.stderr) raise SystemExit(1) except SearchProviderError as exc: _emit(_error_response("provider_error", str(exc), [str(exc)]), getattr(args, "pretty", False), sys.stderr) raise SystemExit(1) except Exception as exc: _emit(_error_response("internal_error", "unexpected error", [str(exc)]), getattr(args, "pretty", False), sys.stderr) raise SystemExit(1)