Markdown · llms.txt

Set this up for me

Give an agent the copy-paste prompt. The steps it must follow are below. They are SETUP.md from the tweet-factory repo, included verbatim at build time (headings shifted down one level so this page keeps a single title).

Human required. Three moments need a person: creating the X app, clicking Authorize in the browser, and pasting any API keys. The agent does the rest.

prompt
Set up tweet-factory on this machine for me.

Read these first, in order, and follow them:
1. https://docs.tweetfactory.sh/llms.txt
2. https://docs.tweetfactory.sh/agent.md
3. https://docs.tweetfactory.sh/install.md
4. https://docs.tweetfactory.sh/x-app.md

agent.md is the same runbook as SETUP.md in https://github.com/abhirupghosh/tweet-factory (plus this prompt). When a step and the repo disagree, trust the repo source and say so.

Rules:
- Install tf with the command in https://docs.tweetfactory.sh/install.md. Do not clone the repo unless the user already has a checkout or asked to develop from source.
- If a checkout is already on this machine, stay in it and use ./setup_x.sh. Do not delete it.
- Never invent credentials, tokens, or API keys. Never print secret values back to me.
- Write secrets only into .env. After writing, run chmod 600 .env. .tokens.json is created by tf auth and is already mode 0600.
- Do every step you can do yourself. Stop and ask me when a step says "Human required".
- After each step, run that step's verification check before continuing.
- tweet-factory writes drafts to files. It does not post to X. Do not post tweets for me.
- Run tf fetch on this machine only. The X refresh token rotates on every use; a second machine invalidates it.

Steps that need me (a human), which you cannot finish yourself:
- Creating an X developer app and copying the OAuth 2.0 Client ID and Client Secret.
- Clicking "Authorize app" in a browser during tf auth.
- Pasting any optional key I choose to provide (Tavily, Notion, Slack, or a direct LLM API key).

Start by installing tf (see the install page) and running `tf --help`. If you are already inside a checkout, run ./setup_x.sh --check instead and tell me what is missing.

Runbook

Included from SETUP.md when this site is built. Edit that file, not a copy.

Setting up tweet-factory for a new user — agent runbook

Audience: a coding agent (Claude Code, Cursor, etc.) setting this up for a user on their machine. Follow these steps in order. Ask the user whenever you need a key, secret, or decision — never guess or invent credentials. Keep the user in the loop; the mechanical work is a few commands plus one browser click from them.

The mechanical work is install.sh for a new machine, or ./setup_x.sh when you are already inside a checkout (Bun/TypeScript — no Python). Your job is to collect the inputs, run the install that matches the machine, help the user fill their persona, and hand them the loop prompt.

Who does what

StepWho
Install tf (script, or ./setup_x.sh when already in a checkout), write .env, chmod 600, fetch, persona file, enrichAgent
Create the X developer app and copy the OAuth 2.0 Client ID and Client SecretHuman
Click Authorize app in the browser during tf authHuman
Paste optional keys (Tavily, Notion, Slack, direct LLM API) if they want themHuman

A copy-paste prompt that points an agent at this runbook lives at docs.tweetfactory.sh/agent-prompt.md.

The GitHub repository is private. Release binaries are still public at https://tweetfactory.sh/download/. Do not treat a binary as a substitute for keeping the source private.


Step 0 — Install tf

Prefer the release binary. It does not clone the repo:

bash
curl -fsSL https://tweetfactory.sh/install.sh | sh
mkdir -p ~/tweet-factory && cd ~/tweet-factory
tf --help

The site build serves install.sh. The script reads https://tweetfactory.sh/download/latest/VERSION, then downloads tf and SHA256SUMS for that tag from https://tweetfactory.sh/download/vX.Y.Z/. TF_VERSION=v0.3.0 pins a tag.

Verify. tf --help exits 0 and prints tweet-factory (tf). command -v tf is on PATH.

If this machine already has a checkout (a directory with setup_x.sh and package.json), stay there. Do not reinstall over it and do not delete it. Run a read-only check first — it reports missing tools and keys without changing anything:

bash
./setup_x.sh --check

Verify. The command exits 0 and prints a tool/key report. It must not create or edit .env.

A binary install has no setup_x.sh. Skip the check and continue with Step 1 in the data directory. tf auth writes .env.example there when it is missing.

Step 1 — Get the user's X API credentials (REQUIRED)

The only hard requirement is an X app with OAuth 2.0 (the sole auth X allows for reading bookmarks). Walk the user through it, then collect the keys by asking them. Do not invent values.

Human required. The user does this in a browser at https://developer.x.com/en/portal/dashboard → their app → User authentication settings:

  • App permissions: Read
  • Type of App: Web App, Automated App or Bot (confidential client → gives a refresh token)
  • Callback URI: http://127.0.0.1:8675/callback (must be exact)
  • Save, then open the Keys and tokens tab and copy the OAuth 2.0 Client ID and Client Secret.

tf auth requests these scopes: tweet.read, users.read, like.read, bookmark.read, offline.access.

Ask the user for:

  • TWITTER_OAUTH2_CLIENT_ID (required)
  • TWITTER_OAUTH2_CLIENT_SECRET (required)

Write them into .env (copy .env.example first if .env doesn't exist). On a binary install, tf auth writes .env.example when it is missing. Never echo secret values back. Then lock the file down:

bash
chmod 600 .env

Verify. Both keys are non-empty, and the mode is 600. This prints only set / missing, never the values:

bash
stat -c '%a' .env   # macOS: stat -f '%OLp' .env
awk -F= '$1=="TWITTER_OAUTH2_CLIENT_ID"{id=$2} $1=="TWITTER_OAUTH2_CLIENT_SECRET"{sec=$2} END{if(length(id)&&length(sec)) print "oauth keys: set"; else {print "oauth keys: missing"; exit 1}}' .env

Step 2 — Optional integrations (ask, offer to skip)

Ask, with a clear skip option. All of these can stay blank:

  • TF_LLM_PROVIDER + a key — only if the user wants direct-API generation instead of the default Claude Code CLI backend. Default tf generate spawns claude -p and needs no key. Providers: anthropic (ANTHROPIC_API_KEY), openrouter (OPENROUTER_API_KEY), openai (OPENAI_API_KEY). Optional TF_LLM_MODEL overrides the model.
  • TAVILY_API_KEY — free at https://tavily.com. Enables tf search, tf enrich, and tf generate --timely. Recommended.
  • NOTION_API_KEY — internal integration at https://www.notion.so/my-integrations. The user must share pages with the integration before tf notion can see them.
  • SLACK_WEBHOOK_URL — https://api.slack.com/messaging/webhooks. If set, drafts can be pushed to Slack.

Human required. Pasting any of these keys. Skipping is a complete setup.

Verify. .env is still mode 600. Do not print the values.

Step 3 — Authorize and back-fill

Once .env has at least the two OAuth 2.0 values:

Checkout ( ./setup_x.sh is in this directory ):

bash
NONINTERACTIVE=1 ./setup_x.sh

A human at the keyboard can run ./setup_x.sh instead and let it ask for anything still blank. It installs Bun, builds tf, then runs the OAuth flow and the first fetch.

Binary install (no setup_x.sh):

bash
tf auth
tf fetch
tf flush || true

Human required. A browser opens (or a URL is printed). The user logs in as the account they want to analyze and clicks Authorize app. On a headless machine, pass --no-browser via tf auth --no-browser and have them open the printed URL. If the callback must be reachable from another machine, set OAUTH_REDIRECT_URI and OAUTH_BIND_HOST and register that exact callback in the X app. The default bind is 127.0.0.1.

The first flush marks everything already in the database as processed, so flush/pending.md starts at 0 new items. Later runs of tf refresh show only what's new.

Verify.

bash
if [ -x ./tf ]; then TF=./tf; else TF=tf; fi
test -s .tokens.json && echo "tokens: present"
test -s tweets.db && echo "db: present"
"$TF" analyze

tf analyze should print a handle and non-zero counts for the sources the account actually has. Has refresh token: true should have appeared in the auth output (the app needs a confidential client).

Step 4 — Fill the persona

setup_x.sh does not write persona/PERSONA.md. A binary install writes persona/PERSONA.template.md on tf auth or tf onboard when the file is missing. Create the persona one of these ways:

  • Copy the template and edit it: cp persona/PERSONA.template.md persona/PERSONA.md
  • Or run tf onboard and press f on the forge screen. That writes persona/PERSONA.md from the four interview answers (what they want to be known for, a hot take, what they're building, three voice words), the account tags, and measured stats.

Make it real — this is what makes drafts sound like the user:

  • Ask: their handle, the lane they want to be known for, voice rules (casing, length, tone), and any banned phrases.
  • Or infer from data: tf analyze summarizes measured voice signals (lowercase-start rate, median length, top liked authors). Confirm the draft with the user before treating it as final.

tf generate still runs with no persona file: it infers from those measured signals. A filled-in persona/PERSONA.md is the voice source of truth.

Verify. persona/PERSONA.md exists, and it is not still the untouched template (@YOUR_HANDLE should be gone). ./tf analyze exits 0.

Step 5 — (Optional) enrich article bookmarks

If TAVILY_API_KEY is set and you want full article text behind their bookmarks:

bash
./tf enrich

Without that key the command errors. Skip the step when the key is absent.

Verify. The command exits 0, or you skipped it because TAVILY_API_KEY is unset.

Step 6 — Hand off the loop prompt

Tell the user how to generate drafts from here. For a one-off batch: ./tf generate. In Claude Code, /tweet-factory runs the bundled skill, which calls tf for data and then drafts.

For a repeating Claude Code loop, pick based on Slack:

With Slack:

/loop 6h Spawn a subagent via the Agent tool with model "opus" to run /tweet-factory end-to-end, then post that run's drafts to Slack via ./tf notify drafts/$(date +%F).md "🧵 fresh tweet ideas". Reply with only a one-line summary. Keep going on this interval.

Terminal only (no Slack):

/loop 6h Spawn a subagent via the Agent tool with model "opus" to run /tweet-factory end-to-end and show me the drafts. Keep going on this interval.

Verify. The user has a command they can run (./tf generate) and, if they wanted Slack, SLACK_WEBHOOK_URL is set. Do not run an infinite loop yourself unless they asked for one.


Reminders

  • Single-host rule: tf fetch (and any cron loop) must run on ONE machine only — the X refresh token rotates on every use; running it on two hosts invalidates the token.
  • Secrets never get committed: .env, .tokens.json, tweets.db, persona/PERSONA.md, flush/*, drafts/*, and onboarding.json are gitignored.
  • Re-auth: if a run logs a 401, run ./tf auth once to mint a new token.
  • Scheduling: tf refresh is the non-LLM refresh (safe to cron). tf cron also runs enrich, so it needs TAVILY_API_KEY or it errors after the fetch. tf update replaces a release binary and is not the database refresh. See deploy and the Deploy section in README.md.

Scheduling on a server is the deploy and cron page.