=============== LIBRARY RULES =============== From library maintainers: - Use README.md and docs/CONTEXT7.md for install, configuration, tool usage, security model, and runtime smoke tests. - Use docs/OBSERVABILITY.md for scripted Hermes runtime checks. - Use docs.xquik.com/guides/hermes-tweet for end-to-end Hermes setup. - Use tweet_explore before tweet_read or tweet_action; tweet_action is disabled unless explicitly enabled. - Use pyproject.toml and the PyPI package hermes-tweet for package metadata and supported Python versions. - Do not ask users to pass credentials through tool arguments; use environment configuration. # Hermes Tweet Hermes Tweet is a native [Hermes Agent](https://github.com/NousResearch/hermes-agent) plugin that brings X/Twitter automation into the Hermes toolset via the [Xquik](https://xquik.com) API. It exposes 99 agent-callable endpoints—covering X search, account reads, tweet posting, replies, likes, retweets, follows, DMs, monitors, webhooks, giveaway draws, extraction jobs, media uploads, and trend reads—as structured Hermes tools that any Hermes-compatible agent can call directly. The plugin registers three tools (`tweet_explore`, `tweet_read`, `tweet_action`) and two slash commands (`/xstatus`, `/xtrends`) into the `hermes-tweet` toolset. Authentication is handled entirely through environment variables—credentials are never accepted through tool arguments. Read endpoints are available whenever `XQUIK_API_KEY` is configured; write/action endpoints are gated behind `HERMES_TWEET_ENABLE_ACTIONS=true` and remain hidden by default, enforcing least-privilege operation. --- ## Installation ### Install via Hermes plugin manager (recommended) ```bash # Interactive install: Hermes will prompt for XQUIK_API_KEY hermes plugins install Xquik-dev/hermes-tweet --enable # Non-interactive: set key manually after install export XQUIK_API_KEY="xq_your_key_here" hermes plugins enable hermes-tweet ``` ### Install from PyPI ```bash # Using pip inside Hermes virtualenv ~/.hermes/hermes-agent/venv/bin/python -m pip install hermes-tweet hermes plugins enable hermes-tweet # Using uv uv pip install --python ~/.hermes/hermes-agent/venv/bin/python hermes-tweet hermes plugins enable hermes-tweet ``` ### Install from local checkout ```bash hermes plugins install file:///absolute/path/to/hermes-tweet --force --enable ``` --- ## Configuration ```bash # Required: Xquik API key (create at https://dashboard.xquik.com) export XQUIK_API_KEY="xq_..." # Optional: override API base URL (default: https://xquik.com) export XQUIK_BASE_URL="https://xquik.com" # Optional: enable write/action endpoints (disabled by default) export HERMES_TWEET_ENABLE_ACTIONS="false" ``` For persistent Hermes sessions, add to `~/.hermes/.env`: ```bash XQUIK_API_KEY=xq_... HERMES_TWEET_ENABLE_ACTIONS=false ``` If Hermes is already running after editing `~/.hermes/.env`, use `/reload` in the interactive CLI, or restart gateway and cron sessions. --- ## Tools ### `tweet_explore` — Search the bundled endpoint catalog Searches the bundled Xquik endpoint catalog locally (no network call). Use this before calling `tweet_read` or `tweet_action` to discover available endpoints, their paths, parameters, and response shapes. ```json // Search for tweet search endpoints { "query": "tweet search", "method": "GET", "limit": 10 } // Response { "success": true, "endpoints": [ { "category": "x", "method": "GET", "path": "/api/v1/x/tweets/search", "summary": "Search tweets", "free": true, "action": false, "responseShape": "Tweet list" } ] } // Filter by category { "category": "draws", "limit": 25 } // Show action (write) endpoints too { "query": "post tweet", "include_actions": true } // Filter to MPP-eligible read endpoints only { "mpp": true, "method": "GET", "limit": 32 } ``` **Schema parameters:** | Parameter | Type | Default | Description | |---|---|---|---| | `query` | string | — | Keyword search across paths, summaries, parameters, response shapes | | `category` | string | — | Filter by endpoint category | | `method` | enum | — | HTTP method filter: GET, POST, PATCH, PUT, DELETE | | `path` | string | — | Exact or partial `/api/v1/...` path filter | | `free` | boolean | — | Filter to free or paid endpoints | | `mpp` | boolean | — | Filter to MPP-eligible endpoints | | `include_actions` | boolean | `false` | Include write-like and private endpoints | | `limit` | integer | `25` | Max results returned (1–100) | --- ### `tweet_read` — Call read-only Xquik endpoints Invokes a single catalog-listed read-only endpoint against the Xquik API. Requires `XQUIK_API_KEY`. Rejects any endpoint marked as `action: true`. ```json // Read account info { "path": "/api/v1/account" } // Response: {"plan": "pro", "credits_used": 142, "credits_total": 5000, ...} // Search tweets with query parameters { "path": "/api/v1/x/tweets/search", "query": { "q": "AI agents", "limit": 25 } } // Look up a user profile { "path": "/api/v1/x/users/elonmusk" } // Get X trends { "path": "/api/v1/x/trends", "query": { "category": "technology" } } // List extraction jobs filtered by status { "path": "/api/v1/extractions", "query": { "status": "completed" } } // Get a specific extraction result { "path": "/api/v1/extractions/550e8400-e29b-41d4-a716-446655440000", "query": { "limit": 100 } } // List tweet drafts { "path": "/api/v1/drafts" } // Check credits balance { "path": "/api/v1/credits" } ``` **Error handling (all tools return consistent JSON):** ```json // Missing API key { "success": false, "error": "XQUIK_API_KEY is not configured." } // Endpoint not in catalog { "success": false, "error": "Endpoint is not in the Hermes Tweet catalog: GET /api/v1/unknown" } // Attempted to call an action endpoint via tweet_read { "success": false, "error": "Use tweet_action for private or write-like endpoints." } // API error { "success": false, "error": "API request failed.", "status_code": 429, "response": {...} } ``` **Schema parameters:** | Parameter | Type | Required | Description | |---|---|---|---| | `path` | string | ✓ | Concrete `/api/v1/...` endpoint path | | `query` | object | — | Query parameters (string, number, or boolean values) | --- ### `tweet_action` — Call write-like or private endpoints Invokes catalog-listed action endpoints (writes, private reads). Disabled unless `HERMES_TWEET_ENABLE_ACTIONS=true`. Always show the endpoint and payload to the user before calling. ```bash # Enable actions first export HERMES_TWEET_ENABLE_ACTIONS=true ``` ```json // Post a tweet { "path": "/api/v1/x/tweets", "method": "POST", "body": { "account": "@myaccount", "text": "Hello from Hermes Tweet! #automation" }, "reason": "Post the user-approved tweet." } // Reply to a tweet { "path": "/api/v1/x/tweets", "method": "POST", "body": { "account": "@myaccount", "text": "Great point! 👍", "reply_to": "1234567890123456789" }, "reason": "Post reply as approved by user." } // Follow a user { "path": "/api/v1/x/follows", "method": "POST", "body": { "account": "@myaccount", "target": "@targetuser" }, "reason": "Follow the user as requested." } // Send a DM { "path": "/api/v1/x/dms", "method": "POST", "body": { "account": "@myaccount", "recipient": "@recipient", "text": "Hi! Thanks for connecting." }, "reason": "Send welcome DM to new follower." } // Run a giveaway draw { "path": "/api/v1/draws", "method": "POST", "body": { "tweet_url": "https://x.com/user/status/1234567890", "winner_count": 3, "require_retweet": true, "require_follow": true }, "reason": "Run giveaway draw for contest tweet." } // Start an extraction job { "path": "/api/v1/extractions", "method": "POST", "body": { "tool_type": "tweet_likers", "target": "1234567890123456789" }, "reason": "Extract likers list for analytics." } // Save a tweet draft { "path": "/api/v1/drafts", "method": "POST", "body": { "text": "Draft tweet text for review later.", "topic": "product launch" }, "reason": "Save draft for later review." } // Compose and score a tweet { "path": "/api/v1/compose", "method": "POST", "body": { "workflow": "score", "text": "Excited to announce our new feature!", "goal": "engagement" }, "reason": "Score draft tweet quality." } ``` **Schema parameters:** | Parameter | Type | Required | Description | |---|---|---|---| | `path` | string | ✓ | Concrete `/api/v1/...` endpoint path | | `method` | enum | ✓ | HTTP method: GET, POST, PATCH, PUT, DELETE | | `reason` | string | ✓ | Brief user-visible reason for the action | | `query` | object | — | Query parameters (string, number, or boolean values) | | `body` | any | — | JSON request body | --- ## Slash Commands ### `/xstatus` — Show account and usage status ```bash # In an active Hermes interactive CLI or gateway session /xstatus # Output: account info, plan, credits used/remaining, subscription status ``` Internally calls `tweet_read` with `path: "/api/v1/account"`. No arguments accepted. ### `/xtrends` — Show current X trends ```bash # All trends /xtrends # Filter by category /xtrends technology ``` Internally calls `tweet_read` with `path: "/api/v1/x/trends"` and an optional `category` query parameter extracted from the command argument. --- ## Python API (internal / programmatic use) ### `register(ctx)` — Plugin entry point The Hermes plugin entry point. Called automatically by Hermes when the plugin is loaded. Registers the three tools, two slash commands, and bundled skill files. ```python # hermes_tweet/__init__.py — called by Hermes Agent automatically from hermes_tweet import register # Hermes calls register(ctx) where ctx is the Hermes plugin context # register(ctx) calls: # ctx.register_tool("tweet_explore", ...) # ctx.register_tool("tweet_read", ..., check_fn=check_api_available) # ctx.register_tool("tweet_action", ..., check_fn=action_enabled) # ctx.register_command("xstatus", handler=xstatus) # ctx.register_command("xtrends", handler=xtrends) # _register_bundled_skills(ctx) # loads skills/hermes-tweet/SKILL.md ``` ### `explore(args)` — Catalog search ```python from hermes_tweet.tools import explore # Search for DM-related endpoints including action endpoints result = explore({"query": "dm", "include_actions": True, "limit": 5}) # Returns JSON string: '{"success":true,"endpoints":[...]}' # Filter to free MPP read endpoints result = explore({"free": True, "mpp": True, "method": "GET"}) ``` ### `call_read(args)` — Call a read endpoint ```python from hermes_tweet.tools import call_read # Get account info result = call_read({"path": "/api/v1/account"}) # Returns JSON string of account data or error # Search tweets result = call_read({ "path": "/api/v1/x/tweets/search", "query": {"q": "hermes agent", "limit": 10} }) ``` ### `call_action(args)` — Call an action endpoint ```python import os os.environ["XQUIK_API_KEY"] = "xq_..." os.environ["HERMES_TWEET_ENABLE_ACTIONS"] = "true" from hermes_tweet.tools import call_action result = call_action({ "method": "POST", "path": "/api/v1/x/tweets", "body": {"account": "@myaccount", "text": "Posted via Hermes Tweet API"}, "reason": "Test post" }) # Returns JSON string: '{"id":"...","text":"...",...}' or error ``` ### `find_endpoint(method, path)` — Catalog lookup ```python from hermes_tweet.catalog import find_endpoint ep = find_endpoint("GET", "/api/v1/x/tweets/search") if ep: print(ep.category) # "x" print(ep.summary) # "Search tweets" print(ep.action) # False print(ep.free) # True print(ep.mpp) # {"credits": "2", ...} or None print(ep.response_shape) # "Tweet list" # Parameterized paths resolve correctly ep = find_endpoint("GET", "/api/v1/extractions/550e8400-e29b-41d4-a716-446655440000") # Matches template: /api/v1/extractions/{id} ``` ### `request(method, path, query, body)` — Low-level HTTP client ```python from hermes_tweet.client import request # Calls Xquik API; reads XQUIK_API_KEY and XQUIK_BASE_URL from environment result = request("GET", "/api/v1/account") # Returns parsed JSON dict or error dict result = request( "POST", "/api/v1/x/tweets", body={"account": "@myaccount", "text": "Hello!"} ) # Auth header: "x-api-key: xq_..." for xq_ keys, "Authorization: Bearer ..." for others ``` --- ## Smoke Test ```bash # Verify plugin loads, explore works without API key, read works with key hermes -z "Use tweet_explore, then read /api/v1/account. Do not call tweet_action." \ --toolsets hermes-tweet # Check registered tools hermes tools list # Verify slash commands in an interactive session or gateway (not -z) # /xstatus # /xtrends technology ``` --- ## Development ```bash # Generate catalog from Xquik OpenAPI spec python scripts/build_catalog.py ../xquik/openapi.yaml # Run full quality gate uv run --python 3.12 --extra dev ruff format --check . && \ uv run --python 3.12 --extra dev ruff check . && \ uv run --python 3.12 --extra dev basedpyright && \ uv run --python 3.12 --extra dev pytest --cov=hermes_tweet --cov=tests \ --cov-report=term-missing --cov-fail-under=100 && \ uv run --python 3.12 --extra dev bandit -c pyproject.toml -r hermes_tweet scripts && \ uv run --python 3.12 --extra dev pip-audit && \ uv run --python 3.12 --extra dev python -m build && \ uv run --python 3.12 --extra dev twine check dist/* ``` --- ## Summary Hermes Tweet is primarily used for two patterns: **social listening / research** and **agent-driven X automation**. In the read-only mode (default), agents use `tweet_explore` to discover endpoints, then call `tweet_read` to perform tweet searches, trend monitoring, account checks, user lookups, giveaway audits, extraction job polling, and draft management—all without any risk of account mutation. This pattern is safe for unattended cron jobs, gateway sessions, and multi-agent pipelines where write access is not needed. For full automation workflows, setting `HERMES_TWEET_ENABLE_ACTIONS=true` unlocks `tweet_action`, enabling agents to post tweets, send replies, manage follows, send DMs, run giveaway draws, trigger extraction jobs, and compose/score content through the Xquik API. The plugin integrates cleanly into Hermes v0.12.0+ as a named toolset (`--toolsets hermes-tweet`), making it straightforward to scope X automation to only the agents and sessions that need it. The bundled skill file (`skills/hermes-tweet/SKILL.md`) provides agents with workflow guidance and safety rules, ensuring the explore-then-act pattern is followed and credentials are never exposed through tool arguments.