# Tweet generator in your voice

tweet-factory is a local private tweet generator and an AI tweet generator that writes in one account’s voice. It is a Bun and TypeScript CLI, `tf`. All rights reserved. Source: [abhirupghosh/tweet-factory](https://github.com/abhirupghosh/tweet-factory). It runs on the machine where you install it. Likes, bookmarks, and your posts go into `tweets.db`. Drafts go into `drafts/`. You publish them yourself. Local files stay the default. Optional cloud sync copies that checkout only after `tf login` and `tf sync push`.

People also search “AI tweet writer” and “AI tweet generator that sounds like me.” Those match this CLI only because the voice comes from your own posts and the model is one you already run. It is not a hosted scheduler, and it is not a claim that the drafts will rank. Claude Code is the default backend; the setup is on [Using tweet-factory with Claude Code](claude-code-twitter.md).

## Where the voice comes from

`src/prompts/voice.ts` builds the system prompt from two files of your data:

- `persona/PERSONA.md`, if it exists. The comment in that module calls it the source of truth for voice. `tf onboard` can write it. You can also edit the template.
- Measured stats from `tweets.db`: the share of posts that start lowercase, the median word count, recurring markers, and the accounts you engage with most.

The instruction in that prompt is to match casing, length, phrasing, and stance, and not to flatten the voice into generic copy. Notion pages you tag, and the flush of new likes and bookmarks, can be attached as fenced reference data. The prompt tells the model that text is data, not instructions.

If there is no persona file yet, the same function says to infer voice from the measured signals.

## Claude Code, or another provider

The default provider is `claude-code`. `src/llm/claudecode.ts` spawns:

```
claude -p <prompt> --output-format json --tools "" --disallowedTools "*,mcp__*" --permission-mode dontAsk
```

No API key is read for that path. It uses the `claude` login already on the machine. `--tools ""` removes built-in tools, so a tweet in the reference data cannot make it run a shell. If `claude` is not on `PATH`, the error tells you to install Claude Code or set `TF_LLM_PROVIDER` and a key.

The other providers, from `src/llm/provider.ts`:

| `TF_LLM_PROVIDER` | Key | Default model in code |
|---|---|---|
| `claude-code` | none | the CLI’s own default |
| `anthropic` | `ANTHROPIC_API_KEY` | `claude-sonnet-4-6` |
| `openrouter` | `OPENROUTER_API_KEY` | `anthropic/claude-sonnet-4.6` |
| `openai` | `OPENAI_API_KEY` | `gpt-4o` |

Direct API calls use temperature 0.8. Claude Code gets a 240 second timeout. More on the [LLM providers](llm.md) page.

## What it will not do

OAuth scopes in `src/x/auth.ts` are `tweet.read`, `users.read`, `like.read`, `bookmark.read`, and `offline.access`. There is no write scope and no command that creates a post. `tf notify` posts a file to Slack when a webhook is set. Publishing on X is a step you take outside `tf`.

The 0–100 number on a draft is computed after the model returns text. `src/rank/score.ts` multiplies an editorial strength on each head by the published weight in `src/rank/weights.ts` (x-algorithm commit cb45b55). It is a local heuristic, not a live For You rank. [How scoring works](how-tweet-factory-scores-drafts.md) has the formula, the four-idea shape, and the calibration rules.

Install the release binary from [Install](install.md). A checkout can still build with `./setup_x.sh`. A coding agent can follow [the runbook](agent.md).

## Related

- [Install](install.md)
- [How the X algorithm works](how-the-x-algorithm-works.md)
- [Compared with Typefully, Tweet Hunter, and Hypefury](compare-typefully-tweet-hunter-hypefury.md)

## FAQ

### What is a tweet generator that uses my own posts?

`tf generate` is that generator. It sends `persona/PERSONA.md` and measured stats from `tweets.db` to your LLM and writes four ideas, each with two or three variants, to `drafts/YYYY-MM-DD.md`. The 0–100 number is filled in locally after the model returns text. You post the variant you want.

### Is there a private tweet generator that runs on your machine?

tweet-factory is proprietary. All rights reserved. Source is at github.com/abhirupghosh/tweet-factory. `tf fetch` stores your X activity in SQLite on that machine. `tf generate` writes drafts to `drafts/YYYY-MM-DD.md`. Nothing in the CLI creates an X post.

### Can Claude Code write the tweets?

Yes, as the default backend. `tf` runs `claude -p` with your existing Claude Code login and with tools disabled. Set `TF_LLM_PROVIDER` to `anthropic`, `openrouter`, or `openai` if you want a direct API instead. You still publish the draft yourself.

### Will the drafts sound like me and rank on For You?

The voice prompt is your persona file plus stats from your own posts, likes, and bookmarks. That is a writing constraint. The score beside a draft is the local per-head heuristic in `src/rank/score.ts`, using the published weights, with a calibration from your impressions when at least eight of your posts have them. The open-source ranker is a different system, described on [the algorithm page](how-the-x-algorithm-works.md). This CLI does not execute it.
