Twitter Personality: what the relaunched Wordware repo actually gives you
AI Agent for Twitter Personality Analysis
At a glance
- What is it?
- An open TypeScript codebase for generating AI personality profiles from an X handle, now rebuilt on the official X API, the Vercel AI Gateway and Neon Postgres. The interesting decisions are in the caching layer and the deliberately unconstrained prompt output, not the viral landing page.
- Who is it for?
- Adopt this if you want a working reference for streaming structured-ish LLM output into a Next.js page with a cache in front of an expensive upstream API, and you are willing to read src/lib/prompts.ts and src/lib/schemas.ts before touching anything. Do not adopt it if you need per-request cost control on X API calls, a documented licence, or a paywall that works out of the box; the paywall is off by default and the repository material does not state a licence.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 44 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem: turning a handle into a profile without scraping it yourself
The repository solves a narrow problem. You have a username, you want a written personality profile of that account, and you do not want to build the pipeline that fetches the profile plus roughly fifteen recent posts, feeds them to a language model, and renders the result as a page with a share image. The README describes exactly that flow: enter one handle, or two for a compatibility check, and the app returns sections such as roast, strengths, love life, spirit animal and pickup lines.
The intended user is a developer or small team that wants a deployable version of this, not a library. It is a Next.js 15 application with a homepage, API routes and branding assets, built by the team behind Sauna. If you want a component to drop into an existing product, this repo is more than you need. If you want the whole product, including the cached historical analyses from the 2024 viral run, this is the whole thing.
Data flow: X API in, Postgres in the middle, streamed JSON out
The mechanism is a four-stage pipeline. First, the profile and about fifteen recent posts are fetched through the official X API v2, with SocialData named as a fallback scraper. Second, that data is cached in Neon Postgres. Third, an LLM generates the analysis and streams it to the browser. Fourth, the result is cached, made shareable, and rendered into dynamic OG images.
The streaming contract is the part worth studying. The README states that /api/analysis and /api/analysis/pair stream raw JSON text, and that the client renders partial JSON as it arrives via src/lib/parse-partial-json.ts. That is a different design from returning a typed object at the end. It means the UI can show sections filling in as tokens land, and it means the client needs a tolerant parser rather than JSON.parse.
Prompt output is deliberately not constrained. The README says prompts enumerate the exact output keys and that structured-output mode is avoided because constrained decoding flattens the voice. src/lib/schemas.ts documents the shapes, and those shapes mirror the cached JSONB analyses key for key. The practical consequence: the prompt, not a schema, is the contract. Rename a key and you break both the parser and the historical rows.
Caching and dedupe: the wordware* columns and their staleness windows
Every generation costs an X API call plus an LLM call, so the repo puts a cache in front of both. The README describes users and pairs rows carrying status flags, with column names prefixed wordware, kept as historical names for data compatibility. Those flags come with staleness windows used to dedupe concurrent generations.
That last phrase is the load-bearing one. Two people submitting the same handle at the same moment should not both trigger a fetch and a generation. The status flags plus a window are how the code arbitrates. The README does not publish the window values, so anyone deploying this should read the code before assuming how fresh a cached analysis is or how long a lock is held.
The historical naming is a real constraint, not a cosmetic one. Because the cached JSONB analyses mirror src/lib/schemas.ts key for key, and the status columns keep their old names, this database is not something you rename casually. The README says so directly: do not change keys casually.
Getting it running: env vars, npm scripts, and the model swap
Setup follows the README. Clone the repository, then run npm install. Copy .env.example to .env.local and fill it in. Run npm run dev.
The required keys are DATABASE_URL for Neon Postgres, AI_GATEWAY_API_KEY for the Vercel AI Gateway (the README notes you can omit it on Vercel because OIDC is automatic), X_API_BEARER_TOKEN for the official X API v2 bearer token, and SOCIALDATA_API_KEY for the fallback scraper. NEXT_PUBLIC_BASE_URL matters more than it looks: share links derive from it, so a wrong value produces wrong links. NEXT_PUBLIC_POSTHOG_KEY and NEXT_PUBLIC_POSTHOG_HOST are optional analytics. STRIPE_* keys are described as paywall plumbing, and the paywall is off by default, controlled in src/lib/config.tsx.
The model is an environment variable, not a code change. AI_MODEL takes any Gateway model string; the README gives xai/grok-4.20-non-reasoning as the default and lists zai/glm-5.2 and openai/gpt-5.2 as examples. That is the cleanest part of the design. It also means output tone is a deployment decision, and the prompts in src/lib/prompts.ts were written against a particular default.
Where this repo is the wrong tool
The X API dependency is the sharp edge. The README describes the bearer token as pay-per-use and points to buying credits in the X developer console. There is no described per-user budget, rate limiter or cost ceiling in front of that call. A public deployment of this app is a public deployment of your X API spend, moderated only by the cache and its staleness window.
Two more limits. First, no licence is stated in the supplied material, which is a problem for anyone planning to reuse the code in a commercial product. Second, the SocialData fallback is a scraper. If your use case has a compliance review, a fallback path that scrapes rather than calls an official API is something to flag before deployment, not after.
There is also a subtler mismatch. The repo carries a paywall that is off by default, Stripe keys, and PostHog wiring. If you only want the analysis pipeline, you are inheriting product scaffolding you will have to remove or ignore.
Alternatives, and the actual difference in approach
The most direct comparison is to skip this application and call the model yourself. You would fetch the profile with the X API, put the text in a prompt, and stream the response. That is perhaps fifty lines and it avoids Next.js, Neon and the wordware column names. What you lose is everything the repo has already solved: the partial-JSON parser, the concurrent-generation dedupe, the pair mode, the OG image rendering, and the cached historical analyses that keep rendering.
A second comparison is to a hosted personality-analysis product. Those hide the prompt, the model and the cache policy. This repo exposes all three, which is the reason to use it and also the reason it is work. If your team cannot read TypeScript and Postgres schema conventions, a hosted tool is the honest choice.
The design choice that separates this repo from a generic LLM wrapper is the refusal to use structured-output mode. Constrained decoding would give you a schema-validated object and a simpler client. The README argues it flattens the voice, so the project accepts a fragile contract in exchange for output that reads like the original viral version. That trade is defensible and it is also the main source of maintenance risk.
Maintenance cost and what the licence gap means
Three things drive ongoing cost. The prompts in src/lib/prompts.ts are tied to output keys that must stay in sync with src/lib/schemas.ts and with the JSONB rows already in the database, so prompt edits are migrations, not copy changes. The model is swappable via AI_MODEL, but a swap changes tone, and there is no described evaluation harness in the repository material to tell you whether a new model still produces the sections the prompts ask for. The X API and SocialData integrations are external services whose pricing and availability are outside the repo's control.
On licensing, the supplied material does not state one. That is not a detail you can defer: without a licence file, the default position is that no rights are granted, regardless of the code being publicly readable. Anyone intending to deploy this beyond personal use should resolve that before writing product code on top of it. This is not legal advice; it is a statement that the repository material does not answer the question.
Upgrades are otherwise conventional Next.js 15 work. The historical cached analyses keep rendering, which is a compatibility promise the README makes explicitly, and it is the reason the wordware column names and the schema keys should be treated as frozen.
Editorial conclusion
Adopt this if you want a working reference for streaming structured-ish LLM output into a Next.js page with a cache in front of an expensive upstream API, and you are willing to read src/lib/prompts.ts and src/lib/schemas.ts before touching anything. Do not adopt it if you need per-request cost control on X API calls, a documented licence, or a paywall that works out of the box; the paywall is off by default and the repository material does not state a licence. Verify three things first: the licence file, whether SOCIALDATA_API_KEY fallback fits your compliance posture, and whether the wordware* status columns in your database match the staleness windows the code expects.
Community notes