Autonomous X (Twitter) posting system. Runs a daily pipeline that researches trending AI topics, writes developer-focused posts, and schedules them throughout the day.
researcher → writer → scheduler → neon DB → pg_cron → X API
-
Researcher — searches X and the web for AI news, model releases, infra patterns, and developer tooling from the last 24 hours. Produces a research brief with 5–8 content angles. Uses
grok-4-latestvia the Responses API withwebSearchandxSearchtools. -
Writer — turns the brief into 4–6 X posts (single tweets or threads). Practical, builder-focused voice: no hype, no emojis, no em dashes. Uses
grok-4-latestwith structured output. -
Scheduler — assigns optimal posting times targeting developer-active windows (8–10 AM, 12–2 PM, 5–7 PM, 9–11 PM EST), minimum 90 minutes apart. Uses
grok-4-1-fast-non-reasoningwith structured output.
Posts are persisted to Neon and published via pg_cron calling /cron/execute-post.
- Vercel AI SDK —
generateText/generateObjectfor all LLM calls - xAI Grok — model provider for all three agents
- Hono — HTTP server
- Neon + Drizzle ORM — Postgres storage
- X API (XDK) — tweet publishing via OAuth1
npm install
cp .env.example .env
# Fill in API keysRequired environment variables:
| Variable | Description |
|---|---|
XAI_API_KEY |
xAI API key for Grok models |
X_API_KEY |
X OAuth1 API key |
X_API_SECRET |
X OAuth1 API secret |
X_ACCESS_TOKEN |
X OAuth1 access token |
X_ACCESS_TOKEN_SECRET |
X OAuth1 access token secret |
DATABASE_URL |
Neon Postgres connection string |
CRON_SECRET |
Shared secret for cron HTTP routes |
# Start server (port 3010, hot reload)
npm run dev
# Test individual agents against real APIs
npm run test:researcher
npm run test:writer
npm run test:scheduler
# Run the full pipeline end-to-end
npm run test:agents
# Trigger cron routes manually
npm run test:cron:daily
npm run test:cron:execute-post| Method | Path | Description |
|---|---|---|
| GET | / |
Health check |
| GET | /cron/daily |
Trigger the full pipeline (async, returns 202) |
| POST | /cron/execute-post |
Publish a scheduled post { postId: number } |
All cron routes require x-cron-secret header (or ?secret= query param) matching CRON_SECRET.
Create a second job on cron-job.org:
- URL:
POST https://<your-vercel-url>/cron/execute-post - Headers:
x-cron-secret: <CRON_SECRET>,Content-Type: application/json - Body: empty
- Schedule: every 30 minutes
No body means scan mode: queries all pending posts with scheduled_at <= NOW() and publishes each.