# Watch

> Watch X (Twitter) accounts and collect their new posts via the official API -- add an account, poll for new posts, read the archive, translate or ad-filter delivery. Holds no logic of its own; it calls the xwatch command, and it confirms every billed X API read (the API is pay-per-post) before running it. Trigger phrases: watch an X account, watch a twitter account, collect new posts, poll x, get this account's latest tweets, backfill an account, X 계정 감시해줘, 트위터 새 글 가져와줘, 이 계정 폴링해줘, 계정 백필해줘.

- Skill: `seokhoonj/watch` (Agent Skill)
- Install (CLI): `npx skillmds@latest add seokhoonj/watch`
- Raw SKILL.md: https://api.skillmd.com/api/skills/seokhoonj/watch/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: seokhoonj (https://skillmd.com/u/seokhoonj)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/seokhoonj/watch

---


# 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:

```sh
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:

```sh
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` (or `xwatch recent @handle
  --limit N`). This still READS the API (billed). Use a small `--limit` unless asked.
- **Start watching an account** -- `xwatch add @handle`. Free-ish (one lookup); begins
  from now, so the first poll only sees posts published afterwards. `--backfill` instead
  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`:

```sh
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. |

