xwatch watch -- collect new posts from watched X accounts
Reading the X API costs money -- it is pay-per-use, about $0.005 per post returned,
billed on every post read (client-side filtering saves nothing; you already paid to read
it). So the discipline of this skill is: know the cost before a read, and confirm the
expensive ones (poll, backfill, recent with a large limit) before running them.
What this skill does not do
It holds no logic. The API calls, the since_id watermark rules, archiving, the text
filter, the LLM classify/translate, and delivery via pushpush are all the xwatch
package's job. Do not reimplement them here or copy the API's pricing/limits into a
script -- a rule that lives in two places drifts. This skill only assembles one xwatch
command and runs it.
Confirm the command is ready
This skill calls the xwatch command the package installs. Once per session, check it:
xwatch --help
If the help prints, you are ready. If command not found appears, do not invent a path;
tell the user to install it:
pip install xwatch # once it is on PyPI
pipx install xwatch # for a global command, kept isolated
If they installed it into a virtual environment, confirm they run it with that env active.
The X API bearer token
Every read needs a bearer token. If a command fails with a "no X API bearer token"
message, the token is not set. Never have the user paste the token into the chat.
Point them at the repo README.md (Set up): store it in credentials.json (a 0600 file)
or export X_BEARER_TOKEN in their own terminal.
Procedure
1. Settle what to do
- Print latest, no store, no watching --
xwatch @handle(orxwatch recent @handle --limit N). This still READS the API (billed). Use a small--limitunless asked. - Start watching an account --
xwatch add @handle. Free-ish (one lookup); begins from now, so the first poll only sees posts published afterwards.--backfillinstead collects the recent timeline on the first poll (billed, up to ~3,200 posts). - Collect new posts once --
xwatch poll. Reads only posts newer than each account's watermark (an idle account costs nothing). - Read the archive (free, local) --
xwatch posts --handle @handle, with--since/--until/--ads/--no-ads. - Judge archived posts as ads (LLM) --
xwatch classify(Gemini free tier by default).
2. Confirm cost before any billed read -- do not skip for the expensive ones
For poll of a fresh account, backfill, or a large recent, the read count (and dollar
cost) can be large. Show the user what will be read before running it:
This will read the X API (billed ~$0.005/post):
action backfill @someaccount
scope up to ~3,200 recent posts (~$16 at the cap)
cap --max-posts 500 (about $2.50)
Run it?
poll over already-watched accounts is cheap (only new posts) and does not need a heavy
confirm; a first-time backfill or an uncapped recent --limit 100 does. When unsure of
the volume, add a cap (backfill --max-posts N) and say so.
3. Run
Run the single xwatch command. Delivery options are flags on poll / watch:
xwatch poll --translate Korean # deliver each new post translated, above the original
xwatch poll --filter-ads # drop LLM-judged promotional posts from delivery
Both are opt-in and also settable in config.toml (translate = "Korean", filter_ads = true) so a scheduled poll picks them up. They need an LLM key (Gemini by default, free)
and degrade to delivering the original on failure.
4. Report the result as-is
xwatch prints one clean line per outcome (e.g. collected 3 new post(s)), and every
expected failure is a one-line xwatch: ... message on stderr, not a traceback. Report it
as printed; do not round a skip or a failure into a success.
When a command fails
The message after xwatch: is already a sentence the user can read -- pass it through.
| Message contains | Action to add |
|---|---|
no X API bearer token |
The token is not set. Point the user at README "Set up"; never take the token in the chat. |
readable by more than its owner |
The credentials file is loose. The message carries the chmod 600 command. |
rate limit (429) |
Transient. A watch keeps going; a one-off poll can be retried later. |
skipping @handle |
One account was passed over (handle no longer resolves, or the API refused it); the rest of the poll still ran. |
needs an API key for <provider> |
The LLM feature (classify/translate/filter-ads) has no key. Point at README; set GEMINI_API_KEY in the environment or credentials file. |
| a config/credentials error | The config dir is unresolved or malformed. Do not create files in the repo; xwatch resolves paths from XDG_CONFIG_HOME -- surface the message and let the user fix the named file. |