Skill: okradze/desearch-ai-search (version 1.0.2)

--- name: desearch-ai-search description: AI-powered search that aggregates and summarizes results from multiple sources including web, X/Twitter, Reddit, Hacker News, YouTube, ArXiv, and Wikipedia. Use this when you need a synthesized answer or curated links from across the internet and social platforms. metadata: {"clawdbot":{"emoji":"🔎","homepage":"https://desearch.ai","requires":{"env":["DESEARCH_API_KEY"]}}} --- # AI Search By Desearch AI-powered multi-source search that aggregates results from web, Reddit, Hacker News, YouTube, ArXiv, Wikipedia, and X/Twitter — returning either summarized answers or curated links. ## Quick Start 1. Get an API key from https://console.desearch.ai 2. Set environment variable: `export DESEARCH_API_KEY='your-key-here'` ## Usage ```bash # AI contextual search (summarized results from multiple sources) desearch.py ai_search "What is Bittensor?" --tools web,reddit,youtube # AI web link search (curated links from specific sources) desearch.py ai_web "machine learning papers" --tools arxiv,web,wikipedia # AI X/Twitter link search (curated post links) desearch.py ai_x "crypto market trends" --count 20 ``` ## Commands | Command | Description | |---------|-------------| | `ai_search` | AI-summarized search across multiple sources. Returns aggregated results with context. | | `ai_web` | AI-curated link search. Returns the most relevant links from chosen sources. | | `ai_x` | AI-powered X/Twitter search. Returns the most relevant post links for a topic. | ## Options | Option | Description | Applies to | |--------|-------------|------------| | `--tools`, `-t` | Sources to search: `web`, `hackernews`, `reddit`, `wikipedia`, `youtube`, `arxiv`, `twitter` (comma-separated) | Both | | `--count`, `-n` | Number of results (default: 10, max: 200) | All | | `--date-filter` | Time filter: `PAST_24_HOURS`, `PAST_2_DAYS`, `PAST_WEEK`, `PAST_2_WEEKS`, `PAST_MONTH`, `PAST_2_MONTHS`, `PAST_YEAR`, `PAST_2_YEARS` | `ai_search` | ## Examples ### Research a topic with AI summary ```bash desearch.py ai_search "What are the latest developments in quantum computing?" --tools web,arxiv,reddit ``` ### Find academic papers ```bash desearch.py ai_web "transformer architecture improvements 2026" --tools arxiv,web ``` ### Get recent news from multiple sources ```bash desearch.py ai_search "AI regulation news" --tools web,hackernews,reddit --date-filter PAST_WEEK ``` ### Find YouTube tutorials ```bash desearch.py ai_web "learn rust programming" --tools youtube,web ``` ### AI-curated X/Twitter links on a topic ```bash desearch.py ai_x "latest AI breakthroughs" --count 20 ``` ## Response ### Example (truncated) ```json { "tweets": [ { "id": "2023465890369728573", "text": "Superposition allows qubits to encode multiple possibilities...", "url": "https://x.com/rukky_003/status/2023465890369728573", "created_at": "2026-02-16T18:33:57.000Z", "like_count": 5, "retweet_count": 0, "view_count": 155, "reply_count": 0, "quote_count": 2, "lang": "en", "is_retweet": false, "is_quote_tweet": true, "media": [], "user": { "id": "1316260427190472704", "username": "rukky_003", "name": "RuqoCrypto 🧠", "url": "https://x.com/rukky_003", "followers_count": 2424, "verified": false, "is_blue_verified": true } } ], "search": [ { "title": "What Is Quantum Computing? | IBM", "link": "https://www.ibm.com/think/topics/quantum-computing", "snippet": "Quantum computers take advantage of quantum mechanics..." } ], "miner_link_scores": { "2023465890369728573": "HIGH", "https://www.ibm.com/think/topics/quantum-computing": "MEDIUM" }, "completion": "Quantum computing uses qubits that leverage superposition and entanglement to compute in fundamentally different ways than classical computers..." } ``` ### Notes - `miner_link_scores` keys are tweet IDs for Twitter results and full URLs for web results. Values are `"HIGH"`, `"MEDIUM"`, or `"LOW"`. - `media` is always an array; empty `[]` when no media is attached. - `completion` is always a string; empty string `""` if summarization fails. ### Errors Status 401, Unauthorized (e.g., missing/invalid API key) ```json { "detail": "Invalid or missing API key" } ``` Status 402, Payment Required (e.g., balance depleted) ```json { "detail": "Insufficient balance, please add funds to your account to continue using the service." } ``` ## Resources - [API Reference](https://desearch.ai/docs/api-reference/post-desearch-ai-search) - [Desearch Console](https://console.desearch.ai)

path: scripts/desearch.py
content: #!/usr/bin/env python3
"""
Desearch AI Search CLI - AI-powered multi-source search with summarized results.

Usage:
    desearch ai_search "<query>" [options]
    desearch ai_web "<query>" [options]
    desearch ai_x "<query>" [options]

Environment:
    DESEARCH_API_KEY - Required API key from desearch.ai
"""

import argparse
import json
import os
import sys
from urllib.error import HTTPError, URLError
from urllib.parse import urlencode
from urllib.request import Request, urlopen

DESEARCH_BASE = "https://api.desearch.ai"

COMMANDS = ["ai_search", "ai_web", "ai_x"]

WEB_TOOLS = ["web", "hackernews", "reddit", "wikipedia", "youtube", "arxiv"]

DATE_FILTERS = [
    "PAST_24_HOURS",
    "PAST_2_DAYS",
    "PAST_WEEK",
    "PAST_2_WEEKS",
    "PAST_MONTH",
    "PAST_2_MONTHS",
    "PAST_YEAR",
    "PAST_2_YEARS",
]


def get_api_key() -> str:
    key = os.environ.get("DESEARCH_API_KEY")
    if not key:
        print("Error: DESEARCH_API_KEY environment variable not set", file=sys.stderr)
        print("Get your key at https://console.desearch.ai", file=sys.stderr)
        sys.exit(1)
    return key


def api_request(
    method: str, path: str, params: dict = None, body: dict = None
) -> dict | str:
    [REDACTED_SECRET])
    url = f"{DESEARCH_BASE}{path}"

    if method == "GET" and params:
        filtered = {k: v for k, v in params.items() if v is not None}
        if filtered:
            url = f"{url}?{urlencode(filtered, doseq=True)}"

    headers = {
        "Authorization": f"{api_key}",
        "Content-Type": "application/json",
        "User-Agent": "Desearch-Clawdbot/1.0",
    }

    data = None
    if method == "POST" and body:
        data = json.dumps(body).encode()

    try:
        req = Request(url, data=data, headers=headers, method=method)
        with urlopen(req, timeout=60) as response:
            raw = response.read().decode()
            try:
                return json.loads(raw)
            except json.JSONDecodeError:
                return raw
    except HTTPError as e:
        error_body = e.read().decode() if e.fp else ""
        try:
            return json.loads(error_body)
        except (json.JSONDecodeError, Exception):
            return {"error": f"HTTP {e.code}: {e.reason}", "details": error_body}
    except URLError as e:
        return {"error": f"Connection failed: {e.reason}"}
    except Exception as e:
        return {"error": str(e)}


def cmd_ai_search(args):
    body = {"prompt": args.query, "streaming": False}
    if args.tools:
        body["tools"] = args.tools.split(",")
    else:
        body["tools"] = ["twitter", "web"]

    if args.count:
        body["result_count"] = args.count
    if args.date_filter:
        body["date_filter"] = args.date_filter
    return api_request("POST", "/desearch/ai/search", body=body)


def cmd_ai_web(args):
    tools = args.tools.split(",") if args.tools else ["web"]
    body = {
        "prompt": args.query,
        "tools": tools,
    }
    if args.count:
        body["count"] = args.count
    return api_request("POST", "/desearch/ai/search/links/web", body=body)


def cmd_ai_x(args):
    body = {"prompt": args.query}
    if args.count:
        body["count"] = args.count
    return api_request("POST", "/desearch/ai/search/links/twitter", body=body)


COMMAND_HANDLERS = {
    "ai_search": cmd_ai_search,
    "ai_web": cmd_ai_web,
    "ai_x": cmd_ai_x,
}


def format_ai_search(data):
    lines = []
    if isinstance(data, str):
        return data
    if "error" in data:
        return f"Error: {data['error']}"

    for key in (
        "search",
        "hacker_news_search",
        "reddit_search",
        "youtube_search",
        "tweets",
    ):
        results = data.get(key)
        if not results:
            continue
        label = key.replace("_", " ").title()
        lines.append(f"\n=== {label} ===")
        for r in results[:10]:
            lines.append(f"\n  {r.get('title', 'No title')}")
            lines.append(f"  {r.get('link', '')}")
            snippet = r.get("snippet", "")
            if snippet:
                lines.append(f"  {snippet[:200]}")

    return "\n".join(lines) if lines else "No results found."


def format_web_links(data):
    lines = []
    if isinstance(data, str):
        return data
    if "error" in data:
        return f"Error: {data['error']}"

    results = (
        data if isinstance(data, list) else data.get("results", data.get("data", []))
    )
    if isinstance(results, dict):
        for source, items in results.items():
            if not isinstance(items, list):
                continue
            lines.append(f"\n=== {source} ===")
            for r in items[:10]:
                lines.append(f"\n  {r.get('title', 'No title')}")
                lines.append(f"  {r.get('link', r.get('url', ''))}")
    elif isinstance(results, list):
        for r in results[:10]:
            lines.append(f"\n  {r.get('title', 'No title')}")
            lines.append(f"  {r.get('link', r.get('url', ''))}")

    return "\n".join(lines) if lines else "No results found."


def format_tweets(data):
    lines = []
    if isinstance(data, str):
        return data
    if isinstance(data, dict) and "error" in data:
        return f"Error: {data['error']}"

    tweets = (
        data if isinstance(data, list) else data.get("tweets", data.get("results", []))
    )
    if not isinstance(tweets, list):
        tweets = [data] if isinstance(data, dict) else []

    for t in tweets[:20]:
        user = t.get("user", {})
        username = user.get("username", user.get("screen_name", "unknown"))
        name = user.get("name", username)
        text = t.get("text", t.get("full_text", ""))
        likes = t.get("like_count", t.get("favorite_count", 0))
        retweets = t.get("retweet_count", 0)
        date = t.get("created_at", "")

        lines.append(f"\n@{username} ({name}) - {date}")
        lines.append(f"  {text[:280]}")
        lines.append(f"  Likes: {likes} | Retweets: {retweets}")

        tweet_id = t.get("id", t.get("id_str", ""))
        if tweet_id:
            lines.append(f"  https://x.com/{username}/status/{tweet_id}")

    return "\n".join(lines) if lines else "No tweets found."


TEXT_FORMATTERS = {
    "ai_search": format_ai_search,
    "ai_web": format_web_links,
    "ai_x": format_tweets,
}


def main():
    parser = argparse.ArgumentParser(
        description="Desearch AI Search CLI - AI-powered multi-source search",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=__doc__,
    )
    parser.add_argument("command", choices=COMMANDS, help="Command to execute")
    parser.add_argument("query", help="Search query")

    parser.add_argument(
        "--count", "-n", type=int, default=None, help="Number of results"
    )
    parser.add_argument(
        "--tools",
        "-t",
        help="Comma-separated list of tools: web,hackernews,reddit,wikipedia,youtube,arxiv,twitter",
    )
    parser.add_argument(
        "--date-filter", choices=DATE_FILTERS, help="Date filter for AI search"
    )

    args = parser.parse_args()

    handler = COMMAND_HANDLERS[args.command]
    results = handler(args)

    if isinstance(results, str):
        print(results)
    else:
        print(json.dumps(results, indent=2))


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)