npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

persona-review

v1.2.0

Published

AI persona feedback tool for web pages — visit a page as a simulated persona and get reactions, friction points, and suggestions.

Readme

persona-review

AI persona feedback for non-profit, advocacy, charity, and social-cause web pages. You give it a URL and a persona; it visits the page and tells you what that persona noticed, liked, found confusing, and where they'd give up. Reactions are written in the page's own language, as a native speaker.

>>> Go to the website <<<


What it does today

  1. Loads the URL you give it in a real headless Chromium (Playwright). Waits for network idle and a small cushion past DOMContentLoaded so the persona doesn't critique a half-booted UI.

  2. Captures the page's accessibility tree, the page language (<html lang>), and a JPEG screenshot of the top viewport.

  3. Runs an agent loop as one of the built-in persona archetypes: observe → choose an action (scroll / click / type) → observe again → repeat. Form submission is blocked. The persona narrates only at the end.

  4. Prints a final review — summary in the persona's voice (in the page's language), plus structured lists of what they liked, what confused them, where the friction is, and whether they'd abandon.

  5. Optional follow-up Q&A (--repl): after the review, you can ask the same persona questions about the same page in an interactive prompt. The persona may scroll/click/type to look again before answering. Or use --repl-only to skip the review and go straight to questions.

  6. Hard cost cap: $1 (configurable) per (URL, persona) session, shared across the review and every follow-up turn. The session aborts when exhausted. A soft action cap (15 actions per phase) is also enforced.

  7. Opt-in form submission (--allow-submit): with explicit consent, the persona fills the form using a shared test identity, clicks submit once per session, and reacts to the resulting thank-you message or error in their final feedback / answer. Copy ./submit-data.yaml to your own file and pass it with --submit-data <path>; editing the bundled submit-data.yaml directly may be overwritten when the software is updated.

It does not: authenticate. Custom personas come from YAML files in your personal .persona-review/personas/ directory.

Scope: every persona assumes some interest in the cause. This tool is not designed for commercial / SaaS pages — for that, the personas would need different goals and frustrations.


Architecture

┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐
│  Web UI         │   │  TUI            │   │  CLI            │
│  (browser)      │   │  (--ui / --tui) │   │  (persona-      │
└────────┬────────┘   └────────┬────────┘   │   review)       │
         │                     │            └────────┬────────┘
         ▼                     │                     │
┌─────────────────┐            │                     │
│  HTTP server    │            │                     │
│  (server.ts)    │            │                     │
└────────┬────────┘            │                     │
         │                     │                     │
         └──────────┬──────────┴─────────────────────┘
                    │
                    ▼
         ┌──────────────────┐        ┌──────────────┐
         │  Playwright      │  ───▶  │  Target URL  │
         │  headless        │        │  (public     │
         │  Chromium        │  ◀──   │   page)      │
         └──────────────────┘        └──────────────┘
                    │ persona + snapshot
                    ▼
         ┌─────────────────┐        ┌──────────────────────────┐
         │  LLM provider   │  ◀──▶  │  Anthropic / OpenAI /    │
         │  adapter        │        │  Google Gemini API       │
         └─────────────────┘        └──────────────────────────┘

Entry points:

| Entry point | Status | How to run | | ------------------------------- | ------- | ------------------------------------------------------- | | CLI (persona-review) | ✅ done | npx persona-review <url> or npm run review -- <url> | | TUI (persona-review --ui) | ✅ done | npx persona-review --ui or npm run review -- --ui | | Web UI (persona-review --web) | ✅ done | npx persona-review --web or npm run review -- --web |


Prerequisites


Install

Choose ONE of the methods bellow. If you prefer you can use the free Google Cloud Shell instead of installing to your computer.

1. Install via npm (use with npx)

You just need to have installed Nodejs version 20 or more.

To install the browser dependency, run this once:

npx persona-review --install-browsers

This downloads the Chromium browser artifact needed by the Playwright version bundled with persona-review. On Linux, it also installs the system packages Chromium needs. Do not use plain npx playwright install chromium for this npm/npx install method; it can install Chromium for a different Playwright package.

In Google Cloud Shell, Playwright's bundled zip extractor hangs inside the gVisor sandbox (the download reaches 100%, then extraction stalls on the first large file). To work around this, persona-review downloads the browser archive itself and extracts it with the system unzip into $HOME/.cache/persona-review-ms-playwright, the only filesystem that persists across Cloud Shell restarts. This needs the unzip package (preinstalled in Cloud Shell; if missing, run sudo apt-get install -y unzip).

On Windows, if you previously saw an error about chromium_headless_shell-... after installing browsers, update persona-review and run npx persona-review --install-browsers again. Current versions use the regular chromium-... browser artifact on Windows, and the headless-shell artifact on macOS and Linux.

To use you need an API key from Anthropic, Open AI or Google:

# macOS / Linux
export ANTHROPIC_API_KEY=sk-ant...
# Windows (PowerShell)
$env:ANTHROPIC_API_KEY="sk-ant..."
# Windows (Command Prompt)
set ANTHROPIC_API_KEY=sk-ant...

npx persona-review https://example.org/

npx downloads and runs the latest published version automatically. All options described in Usage work the same way — replace npm run review -- with npx persona-review throughout.


2. Install the development version

Use this if you want to modify the source or contribute:

git clone https://codeberg.org/osvik/persona-review.git
cd persona-review
npm install
npm run build
npm run review -- --install-browsers

npm install pulls the dependencies; npm run build compiles TypeScript to dist/ and marks the CLI executable; npm run review -- --install-browsers downloads the browser for the local Playwright dependency.

To use you need an API key from Anthropic, Open AI or Google:

# macOS / Linux
export ANTHROPIC_API_KEY=sk-ant...
# Windows (PowerShell)
$env:ANTHROPIC_API_KEY="sk-ant..."
# Windows (Command Prompt)
set ANTHROPIC_API_KEY=sk-ant...

npm run review -- https://example.org/

3. Install using Docker

If you can't use another method, you can use this software inside a Docker container. You need to install Docker first!

This method takes much longer to install, as you have to download the Docker image, but it's reliable.

Create the container from the terminal (just once):

docker run -it --init --ipc=host --name persona-review node:20-bookworm /bin/bash

Install inside your container (just once):

npx persona-review --install-browsers

The official node:20-bookworm image runs as root by default, so this command can install both the browser artifacts and the Linux packages Chromium needs.

Test it with your API key:

export ANTHROPIC_API_KEY=sk-ant...
npx persona-review https://example.org

Of course, you can alternatively use an Open AI or Google Keys.

Use:

Now you can use it by opening Docker desktop's terminal in the conainer persona-review:

bash
export ANTHROPIC_API_KEY=sk-ant...
npx persona-review http://example.org

Configure the LLM provider

By default the CLI calls Anthropic's API directly. Create your API key and add it to the console with:

# macOS / Linux
export ANTHROPIC_API_KEY=sk-ant-...
# Windows (PowerShell)
$env:ANTHROPIC_API_KEY="sk-ant-..."
# Windows (Command Prompt)
set ANTHROPIC_API_KEY=sk-ant-...

To use OpenAI instead, create your API key set OPENAI_API_KEY and pass --provider openai:

# macOS / Linux
export OPENAI_API_KEY=sk-...
# Windows (PowerShell)
$env:OPENAI_API_KEY="sk-..."
# Windows (Command Prompt)
set OPENAI_API_KEY=sk-...

npm run review -- https://example.org --provider openai

To use Google Gemini instead, create your API key, set GEMINI_API_KEY, and pass --provider google:

# macOS / Linux
export GEMINI_API_KEY=...
# Windows (PowerShell)
$env:GEMINI_API_KEY="..."
# Windows (Command Prompt)
set GEMINI_API_KEY=...

npm run review -- https://example.org --provider google

Environment variables take precedence, but you can also store API keys in ~/.persona-review/keys.yaml. The file is created empty the first time persona-review looks for an API key and it does not already exist.

ANTHROPIC_API_KEY: sk-ant-...
OPENAI_API_KEY: sk-...
GEMINI_API_KEY: ...

Use the same key names as the environment variables. You only need to fill in the providers you use.

The CLI reads only the key for the selected provider; nothing else leaves your machine except the HTTP request to the selected LLM provider and the page-load request to the target URL.

Check which providers are ready, which model ids are built in, and where defaults come from:

npm run review -- --status

This checks whether each provider has a key in the environment or ~/.persona-review/keys.yaml; it does not print API key values.

Model: Anthropic defaults to claude-sonnet-4-6; OpenAI defaults to gpt-5.4; Google defaults to gemini-3.1-pro-preview-customtools. Override any provider with --model, e.g. --model claude-opus-4-7, --model gpt-5.5, or --model gemini-3.1-pro-preview.


Usage

Important: All examples below use the development npm run review -- form. If you installed via npm, replace that prefix with npx persona-review:

npm run review -- https://example.org/   # development install
npx persona-review https://example.org/  # npm install

Help

npm run review -- --help
# or
npx persona-review --help

Version

npm run review -- --version
# or
npx persona-review --version

Status

npm run review -- --status
# or
npx persona-review --status

Prints whether ANTHROPIC_API_KEY, OPENAI_API_KEY, and GEMINI_API_KEY are available from the environment or ~/.persona-review/keys.yaml, without showing their values. It also lists the model ids accepted by --model with built-in pricing, then shows every effective default and whether it came from the software or the user defaults file.

User defaults

On first run, persona-review creates an empty defaults file and an empty personal personas directory:

  • macOS / Linux: $HOME/.persona-review/defaults.yaml
  • macOS / Linux: $HOME/.persona-review/personas/
  • Windows: %USERPROFILE%\.persona-review\defaults.yaml
  • Windows: %USERPROFILE%\.persona-review\personas\

Leave it empty to use the built-in defaults. Add YAML keys there to change your normal defaults without editing source code or rebuilding, which is especially useful with npx persona-review.

Precedence is:

  1. Built-in software defaults.
  2. User defaults from defaults.yaml.
  3. Options passed on the command line.

Example:

persona: evidence-and-accountability-checker
provider: openai
model: gpt-5.4
device: desktop
cost_cap_usd: 2
max_actions: 20
max_tokens: 4096
full_page_snapshot: true
allow_downloads: true
allow_cross_page_navigation: false # set true to let personas follow links to other pages
submit_data: /Users/me/.persona-review/submit-data.local.yaml

Then a shorter command uses those defaults:

npx persona-review https://example.org/

Command-line options still win for that run:

npx persona-review https://example.org/ --persona time-pressed-task-completer --provider anthropic

For boolean defaults set to true, use the matching --no-... flag to turn them off for one run, for example --no-json, --no-repl, --no-allow-submit, --no-allow-downloads, --no-full-page-snapshot, or --no-allow-cross-page-navigation.

Supported defaults keys are persona, provider, model, device, json, repl, repl_only, allow_submit, allow_downloads, allow_cross_page_navigation, submit_data, yes, max_tokens, max_actions, cost_cap_usd, and full_page_snapshot. The equivalent long flag names with hyphens, such as max-tokens and allow-downloads, are also accepted.

Basic review with Anthropic model

npm run review -- https://example.org/

List the personas

npm run review -- --list-personas

Uses the default persona (newcomer-orientation-seeker).

Pick a persona

npm run review -- https://example.org --persona evidence-and-accountability-checker
npm run review -- https://example.org --persona time-pressed-task-completer

Different language

The page's language is detected from <html lang> and the LLM responds as a native speaker of that language. No flag needed.

JSON output

npm run review -- https://example.org --json > review.json

--json cannot be combined with --repl or --repl-only.

Follow-up questions (interactive REPL)

After the review, ask the same persona questions about the same page:

npm run review -- https://example.org --persona evidence-and-accountability-checker --repl
── Mei's feedback ── (review prose…)

[follow-up REPL — ask Mei a question. Type "exit" or press Ctrl-D to leave.
 Cost cap shared with the review: $0.78 of $1.00 remaining.]

> Ask Mei: would a clearer financial breakdown change your mind?

Mei: Probably yes — what I want is a single page that shows where the money
goes. The current "About" section gestures at it but the numbers aren't there.

[1 action(s) — $0.27 of $1.00 cap — $0.73 remaining]

Skip the review entirely and go straight to questions:

npm run review -- https://example.org --persona evidence-and-accountability-checker --repl-only

The browser session, page state, and conversation history are reused across every question, so the persona can scroll/click/type to look again. The cost cap is per (URL, persona) session and accumulates across the review plus every follow-up turn.

Allow form submission (--allow-submit)

By default, form-submit buttons are blocked at the browser layer. Pass --allow-submit to let the persona fill the form, click submit once, and react to the resulting thank-you / error / validation page. Submission is hard-capped at one per (URL, persona) session.

Before submitting real forms, copy the bundled identity template and pass your copy with --submit-data. Do not edit submit-data.yaml in place: updates may overwrite that file.

cp submit-data.yaml submit-data.local.yaml
npm run review -- https://example.org --persona regular-supporter-donor --allow-submit --submit-data ./submit-data.local.yaml

The CLI prints a consent prompt summarizing the test identity and target URL, then waits for y / yes before opening the browser:

=== --allow-submit: form submission ENABLED for this run ===

Target URL: https://example.org/petition
Persona:    Daniel (regular-supporter-donor)

Source: ./submit-data.local.yaml (pass --submit-data <path> to use your copy)
Test identity that will be typed into form fields:

  Name:    Daniel PersonaReview
  Email:   [email protected]
  Phone:   +49 30 12345678
  Address: Teststrasse 1, PersonaReview test record, 10115 Berlin, Germany
  Card:    4242 4242 4242 4242 (exp 12/2030)
  IBAN:    DE89 3704 0044 0532 0130 00
  Donation: 5 EUR (one-off)
  ...

This may create a real record in the target site's CRM, marketing
automation, or analytics. Records will be findable by the name and email
above; delete them after the run.
Hard limit: at most one successful submission per session.

Continue and submit the form? [y/N]

Customize the identity by duplicating submit-data.yaml, editing your copy, and passing it with --submit-data /path/to/your.yaml or --submit-data /path/to/your.yml. Do not edit the bundled submit-data.yaml directly because software updates may overwrite it. All personas share the same identity so you don't end up with one CRM record per persona — search the CRM for PersonaReview and the test email after the run, then delete.

Country-specific fields. The four typed sub-sections (identity, address, payment, donation) accept extra keys beyond the documented ones, so you can add a Spanish DNI, a German VAT number, a US SSN, etc. without changing the code. They're surfaced to the LLM with their YAML key as the label:

identity:
  first_name: null # falls back to the persona's name
  last_name: "PersonaReview"
  email: "[email protected]"
  phone: "666666666"
  dni_o_nie: "08966195J" # custom Spain field — picked up automatically

address:
  line1: "Calle de las pruebas"
  line2: "66 ESC INT 1A"
  city: "Madrid"
  postcode: "28007"
  country_code: "ES"
  country_name: "España"

The persona will use these values verbatim when filling form fields it recognizes by label.

For automated runs (CI etc.) pass --yes to skip the prompt:

npm run review -- https://example.org --allow-submit --submit-data ./submit-data.local.yaml --yes

All flags

npm run review -- --ui
npm run review -- <url> [options]
npm run review -- --list-personas
npm run review -- --status
npm run review -- --version

  --persona <id>           Persona archetype id (default: newcomer-orientation-seeker).
  --provider <name>        LLM provider: 'anthropic', 'openai', or 'google'
                           (default: anthropic).
  --device <m|d>           Override the persona's device: 'mobile' or 'desktop'.
  --status                 Show provider readiness, available --model ids,
                           and default sources.
  --list-personas          Print available personas and exit.
  --json                   Emit JSON instead of prose. Mutually exclusive
                           with --repl / --repl-only.
  --repl                   After the review, enter an interactive prompt for
                           follow-up questions. Cost cap is shared.
  --repl-only              Skip the review; load the page and go straight
                           to the interactive prompt.
  --allow-submit           Permit ONE form submission this session. Persona
                           fills the form with the shared test identity and
                           reacts to the resulting page. Requires consent.
                           Copy submit-data.yaml and pass the copy with
                           --submit-data; edits to the bundled file may be
                           overwritten by updates.
  --allow-downloads        Permit browser downloads. Default: downloads are
                           blocked by Playwright.
  --allow-cross-page-navigation
                           Permit persona clicks to navigate away from the
                           reviewed URL. Default: blocked; same-page anchors
                           and non-link UI controls still work.
  --submit-data <path>     Use your copied .yaml/.yml test identity file
                           (default template: ./submit-data.yaml).
  -y, --yes                Skip the --allow-submit consent prompt.
  --model <id>             Provider-specific model id (defaults:
                           Anthropic claude-sonnet-4-6, OpenAI gpt-5.4,
                           Google gemini-3.1-pro-preview-customtools).
  --cost-cap-usd <n>       Hard cost cap in USD per (URL, persona) session
                           (default: 1.0). Includes review + all REPL turns.
  --max-actions <n>        Soft cap on browser actions per phase (default: 15).
  --max-tokens <n>         Max output tokens per LLM call (default: 4096).
  --full-page-snapshot     Send a full-page screenshot each turn (default:
                           viewport only — the persona must scroll to see more).
  --no-<boolean-flag>      Disable a boolean option set in user defaults
                           for this run, e.g. --no-json or --no-repl.
  --ui, --tui              Launch the interactive terminal UI instead of
                           running a single review. Requires a TTY (over
                           SSH use `ssh -t`). Cannot be combined with
                           --json. See "Terminal UI (TUI)" below.
  -v, --version            Show the package version.
  -h, --help               Show help.

Device profile per persona

Each persona has a device field which determines the viewport and user agent:

| device value | Viewport | User agent | Touch | | -------------- | ---------------------- | ------------------- | ----- | | mobile | 390×844 (iPhone-class) | iOS Safari 18 | yes | | desktop | 1280×800 | Chrome 126 on macOS | no | | either | resolves to desktop | Chrome 126 on macOS | no |

Pass --device mobile or --device desktop to override the persona's default.

Example output (prose)

── Marta's feedback ──

I came here because a friend sent the link and said I should sign. The big
photo of the forest is beautiful, and the headline is clear. I like that I
can tell within two seconds what this is about and what they want from me.
But the form asks for my phone number and I don't love giving that out —
especially without a clear word on what they'll do with it.

Liked: clear purpose, strong imagery, short form

Confused by: what happens after I sign, why they need a phone number.

Abandoned:   no — I'd probably sign, but reluctantly.

Friction:
  - [medium] phone number field: "I don't want calls from an organisation"
  - [low] privacy link in the footer: "it's far away from the form itself"

(Illustrative — actual output varies, because persona reactions are not seeded. See "Reproducibility" below.)

Example output (JSON)

{
  "persona_id": "marta-smb-owner",
  "url": "https://example.com",
  "summary": "...",
  "liked": ["..."],
  "confused_by": ["..."],
  "friction": [{ "where": "...", "severity": "high", "quote": "..." }],
  "abandoned_at": null,
  "accessibility_issues": ["..."],
  "trust_signals": { "positive": ["..."], "negative": ["..."] },
  "trace": [{ "step": "...", "reaction": "..." }]
}

Terminal UI (TUI)

If you'd rather not compose long flag strings, launch the interactive terminal UI:

npx persona-review --ui
# or, in a development checkout:
npm run review -- --ui

The TUI lets you pick the URL, persona, and device interactively, browse the persona library with role summaries, run a review and watch its progress, then chat with the persona in the same session. It reads the same defaults (~/.persona-review/defaults.yaml), personas (built-in + ~/.persona-review/personas/), and API keys (env vars or ~/.persona-review/keys.yaml) as the CLI.

Requirements:

  • An interactive terminal (TTY). Over SSH, connect with ssh -t.
  • All the other CLI requirements (Node 20+, Chromium via --install-browsers, an API key for the selected provider).

Inside the TUI:

  • / and Enter navigate menus.
  • p from the main menu opens the full persona list with descriptions.
  • s from the main menu opens the Settings screen.
  • During the review, status lines stream live; the final feedback and cost line are rendered when it completes.
  • After the review, press r to chat (REPL) with the same persona, n to start a new review, or q to quit. In chat, type exit (or press q) to leave.
  • Ctrl-C quits cleanly and closes the browser session.

The Settings screen lets you pick the LLM provider and model, manage your API keys, toggle form submission (with a consent screen) / browser downloads / cross-page navigation / full-page snapshot, pick a custom submit-data file, and edit the cost cap, max actions per phase, and max output tokens. Changes are session-only by default — restarting the TUI returns everything to whatever ~/.persona-review/defaults.yaml says. The Settings screen has a final "Save current settings as default" row: pressing Enter on it writes the Settings-screen fields (provider, model, all toggles, submit-data path, cost cap, max actions, max tokens) to ~/.persona-review/defaults.yaml in the same snake_case format the CLI documents, so subsequent CLI runs pick up the same defaults. Persona and device live in the form, not in Settings, and aren't saved by this action — edit defaults.yaml directly to set them as defaults. The URL is per-run and never saved. API keys are handled separately by the API keys screen — see below. When any of submit / downloads / cross-page navigation is on, the main form shows a warning line so you can't miss it.

The API keys screen shows the status of ANTHROPIC_API_KEY, OPENAI_API_KEY, and GEMINI_API_KEY (each either set (source, last 4: …xxxx) or missing). Editing a key uses a masked input — press Tab to peek at what you typed. Submitting an empty value clears the entry. The key value itself never displays in full; only the last four characters surface for verification. If a key is set via environment variable, the TUI warns you that env vars override the file so a TUI write won't take effect until the env var is unset.

When --allow-submit is enabled, pressing Run opens a dedicated consent screen showing the target URL, persona, the test identity that will be typed into form fields, and a final yes/no choice — the same safety prompt the CLI shows. The default is No, cancel.

If the API key for the selected provider is missing, a red banner appears above the menu and Run is blocked until you set it (export the env var, add it to ~/.persona-review/keys.yaml, or use the in-TUI API keys editor).

From the persona list, press Enter on any persona to open the inspector and view its raw YAML — including built-in personas and any custom ones you've dropped into ~/.persona-review/personas/. Use ↑↓ / PgUp / PgDn / g / G to scroll through long files.

The TUI is theme-safe: it uses colors that read well on both dark and light terminal backgrounds and never paints a background of its own.

--ui cannot be combined with --json. The TUI now covers the full review workflow — including provider/model switching, in-TUI API-key editing, and the persona inspector. The only remaining roadmap item is a log viewer for past runs, which depends on first adding run-log writing to the CLI — see AGENTS.md for the open question.


Web UI

A browser-based review interface is included in this project. It serves the static public/ app and uses src/server.ts to run the review backend.

Warning: The web server provides no authentication. Anyone who can access it can use it with your API keys. Do not expose the web server to the public internet or use it in shared environments. It is intended for local, single-user development only.

Run the web server locally:

npx persona-review --web

Then open:

http://localhost:3000

The web UI lets you choose the target URL, persona, provider, model, and optional review settings in your browser. It shows live review progress, logs, browser screenshots, and the final persona feedback. After the review completes, you can ask follow-up questions in the same session.

The web server uses the same API-key sources as the CLI/TUI: environment variables or ~/.persona-review/keys.yaml. The server loads .env if present and listens on the port configured by PORT (default 3000).

If running from source, make sure dist/server.js exists by running npm run build first.


Persona file format

Built-in personas live as YAML files in this package's personas/ directory. Custom user personas live in $HOME/.persona-review/personas/ on macOS/Linux or %USERPROFILE%\.persona-review\personas\ on Windows. Each file is validated against the Zod schema in src/persona.ts. Example:

id: newcomer-orientation-seeker # unique slug
name: Aisha # illustrative; LLM adapts in any language
age: 27 # optional
role: New to the cause and deciding whether to engage
cause_engagement: casual # neutral | casual | regular | committed
scrutiny: low-medium # low-medium | medium | high
goals:
  - understand what this organization does in 30 seconds
  - decide whether it feels legitimate before doing anything
motivations:
  - wants to feel their small action could make a concrete difference
  - responds to human stories when they are backed by clear proof
frustrations:
  - jargon and acronyms
  - vague claims with no evidence
behaviours:
  - scans the headline, first call to action, and first proof point before reading
  - hesitates if commitment is requested before impact or legitimacy is clear
tech_confidence: medium # low-medium | medium | medium-high (no extremes)
device: either # mobile | desktop | either
accessibility: [] # e.g. [larger-text, screen-reader]
reading_level: general # general | detailed
voice: curious, open-minded, easily discouraged by unclear entry points

Notes:

  • No locale field. Language is auto-detected from the page's <html lang> at runtime, so each persona works in any language.
  • motivations and behaviours are optional for custom personas. If omitted, they default to empty arrays, so older custom persona files still load.
  • tech_confidence is restricted to a middle band — no zero-tech and no power-user, by design.
  • We describe UX-relevant traits (goals, motivations, frustrations, behaviours, tech confidence, device, accessibility needs) and avoid demographic caricature.

You can drop your own custom YAML files into your personal personas directory and they'll be picked up by --list-personas and --persona <id>. If a custom persona uses the same id as a built-in persona, the custom persona wins. If you edit the package's built-in files, they may be overwritten when you update the software.


The persona library

Built-in archetypes, most assuming at least some interest in the cause. The deadline journalist is neutral toward the organization and cause, but has a practical reason to use press material accurately. The fundraising and visual design specialists are expert lenses for teams that want a more professional critique.

| id | who | device | tech | engagement | scrutiny | | ------------------------------------- | -------------------------------------------------------------- | ------ | ----------- | ---------- | ---------- | | newcomer-orientation-seeker | Aisha — new to the cause and deciding whether to engage | either | medium | casual | low-medium | | time-pressed-task-completer | Sofia — arrives with intent and wants to finish quickly | either | medium | regular | low-medium | | regular-supporter-donor | Daniel — gives occasionally and may consider recurring support | either | medium | regular | medium | | evidence-and-accountability-checker | Mei — checks evidence, transparency, and accountability | either | medium-high | regular | high | | deadline-journalist | Nadia — needs accurate, usable material on deadline | either | medium-high | neutral | high | | advocate-and-sharer | Femi — wants to act, share, and bring others in | either | medium-high | committed | medium | | plain-language-reader | Anna — prefers everyday words | either | medium | regular | medium | | accessibility-focused-reader | Yusuf — uses assistive and adaptive settings | either | medium | regular | medium | | help-seeker | Rosa — needs support for themselves or someone close | either | medium | regular | medium | | legacy-and-planned-giving-prospect | Margaret — considers major or planned giving | either | low-medium | committed | high | | marketing-fundraising-specialist | Ines — reviews fundraising and engagement pages | either | medium-high | committed | high | | visual-design-specialist | Kenji — reviews visual composition | either | medium-high | committed | high |

Run npm run review -- --list-personas for the same list with full role descriptions.


Reproducibility

Two runs against the same URL with the same persona will produce different reactions. That is intentional — it simulates natural human variability. Don't treat a single review as canonical; run it twice, look at what both reviews agree on, and treat the overlap as the signal.


Scope and safety

  • Public pages only. Authenticated pages are out of scope.
  • Form submission is opt-in. Default is no submission — submit buttons are blocked at the browser layer. With --allow-submit (and confirmed consent), the persona fills the form using the shared test identity and submits at most once per session. Copy submit-data.yaml, edit your copy, and pass it with --submit-data; direct edits to the bundled file may be overwritten by software updates. The same test identity is used across all personas so records stay easy to find and delete in the target site's CRM.
  • Downloads are opt-in. Default is no browser downloads. Pass --allow-downloads to let Playwright accept downloads for the session. Accepted downloads stay in Playwright's temporary browser storage; this tool does not save them into the project, and they are deleted when the browser context closes.
  • Cross-page navigation is opt-in. Default reviews stay on the URL being reviewed. Links that would leave that URL are blocked at the browser layer, while same-page anchors, cookie banners, tabs, accordions, and other non-link controls still work. Pass --allow-cross-page-navigation when a review should intentionally follow links to other pages.
  • Local-only. No servers we operate. Traffic goes only to the target URL and to the selected LLM provider API.
  • Cost cap. Every session enforces a hard USD cap (default $1, override with --cost-cap-usd). The cap is per (URL, persona) session — shared across the review and every follow-up REPL turn — and aborts the run when exhausted, so a runaway agent cannot burn unbounded spend.

Development

Important: before you do any development, note that this software is licensed under the GNU Affero General Public License v3.0. Any modification of the software, or cloud use such as providing access through a graphical user interface or MCP server, must comply with the requirements of this license.

Build it

npm run typecheck    # type-check without emitting
npm run build        # compile to dist/
npm run review -- <url>   # run the compiled CLI

Source layout:

src/
  agent.ts        # PersonaConversation orchestration, review loop, REPL turns,
                  #   provider/model selection, action caps, and submit cap.
  browser.ts      # Playwright browser session: open, observe, scroll, click,
                  #   type, close; blocks submits, downloads, and cross-page
                  #   navigation unless enabled.
  cli.ts          # CLI entry: flags, persona loading, provider key checks,
                  #   --allow-submit consent, JSON/prose output, REPL.
  cost.ts         # Per-provider/model pricing and CostTracker cap enforcement.
  defaults.ts     # User defaults file creation/loading and option validation.
  keys.ts         # API key lookup: environment variables and ~/.persona-review/keys.yaml.
  persona.ts      # Persona Zod schema + YAML loader/listing helpers.
  review.ts       # Feedback/follow-up schemas, tool schemas, and system prompt
                  #   builder including conditional submission policy.
  server.ts       # Express web server: REST API routes (/api/config, /api/review/*),
                  #   static file serving for public/, and session management.
  submit-data.ts  # Test-identity Zod schema + loader; resolves first_name to
                  #   the persona's name when YAML leaves it null.
  user-config.ts  # Cross-platform paths for ~/.persona-review resources.
  llm/
    types.ts      # Shared provider-neutral message, tool, usage, and client
                  #   interfaces.
    anthropic.ts  # Anthropic Messages adapter.
    google.ts     # Google Gemini generateContent adapter.
    openai.ts     # OpenAI Responses API adapter.
  tui/
    index.tsx     # TUI entry: mounts the Ink app, wires CLI options.
    app.tsx       # Top-level Ink component: routing between screens.
    state.ts      # TUI state store: form fields, review status, settings.
    theme.ts      # Color definitions shared across TUI components.
    validate.ts   # TUI-specific input validation helpers.
    components/
      CostLine.tsx   # Cost display component during/following review.
      Feedback.tsx   # Rendered persona feedback output.
      KeyHint.tsx    # Keyboard shortcut hint bar.
      StatusLog.tsx  # Live status lines during review execution.
    screens/
      apiKeys.tsx          # API key management screen.
      form.tsx             # Main URL/persona/device input form.
      personaInspector.tsx # Raw YAML persona file viewer.
      personaList.tsx      # Persona library browser with descriptions.
      repl.tsx             # Follow-up chat screen.
      review.tsx           # Running review progress and results display.
      settings.tsx         # Provider/model/toggles/settings screen.
      submitConsent.tsx    # --allow-submit consent prompt screen.
personas/
  *.yaml          # 12 archetype files; drop your own in here too
submit-data.yaml  # Default shared test identity template for --allow-submit
                  #   copy it and pass the copy with --submit-data
public/
  index.html      # Web UI entry point.
  index.js        # Frontend JavaScript: UI logic, API calls, polling, PDF export.
  index.css       # Stylesheet (Stripe-inspired design theme).
.env              # Local environment variables (PORT, API keys) — not committed.
.env.example      # Template documenting available env vars.
scripts/
  build.mjs       # Build script: runs tsc to compile src/ to dist/.
package.json      # Package metadata, CLI bin, npm scripts, dependencies.
tsconfig.json     # TypeScript compiler settings.
LICENSE           # GNU Affero General Public License v3.0.

About the TUI

The TUI is built with Ink 5.2.1 (React for terminals) and React 18.3.1. Each screen in src/tui/screens/ is an Ink component rendered to a <Box>-based layout; state flows through a plain zustand-style store in state.ts. The ink-select-input and ink-text-input packages handle menus and masked text fields. Components use Ink's useInput hook for custom keybindings and useStdin / useStdout for raw terminal access. The TUI is launched by cli.ts when --ui or --tui is passed — it delegates to tui/index.tsx which mounts the Ink app, then falls back to the CLI review path on exit.