Skip to main content
Glama
README.md
<div align="center">

# reviewzy

**human-in-the-loop mcp review for user-facing text: agents file, a human approves, later sessions apply**

bulk-audit and rewrite user-facing copy across CLIs, TUIs, web UIs, and websites; prose authorship stays human

[![license](https://shields.uwuclxdy.dev/badge/license-AGPL--3.0-blue)](#license)
[![mcp](https://shields.uwuclxdy.dev/badge/mcp-2026--07--28-orange)](https://modelcontextprotocol.io)

</div>

---

reviewzy is a self-hosted dashboard and mcp server for human-in-the-loop copy review. a coding agent files the exact text it wants to write or rewrite. a human authors or approves each one on the dashboard. a later agent session fetches the approved words and applies them in place. no i18n keys, no content layer: each entry points at the text where it already lives.

file an entry:

```json
{
  "project": "my-app",
  "entries": [
    {
      "repo": "https://github.com/me/my-app",
      "file": "src/cli/main.ts",
      "anchor_text": "error: something went wrong",
      "agent_draft": "error: could not reach the reviewzy daemon",
      "file_content": "#!/usr/bin/env bun\nconsole.error(\"error: something went wrong\")\n",
      "constraints": { "max_len": 48, "tone": "plain, lowercase" }
    }
  ],
  "filed_by": "help-text-audit"
}
```

the call answers with the entry as `draft` and a dashboard link for the batch:

```json
{
  "batch_id": "01M060XY9GNK6ANVENN4Y4MK7N",
  "results": [
    { "id": "01M060XY9GNK6ANVENN4Y4MK7P", "status": "draft", "deduped": false, "updated": false }
  ],
  "dashboard_url": "http://127.0.0.1:3123/?project=my-app"
}
```

## Why

- **file + snippet anchors, no i18n keys:** apply-back is a targeted replace in place
- **humans author, agents apply:** the status machine is server-enforced; only the dashboard reaches `approved` or `rejected`
- **per-project style guide (mcp resource):** merged with the global guide, embedded in `fetch_approved`
- **append-only revision history:** every save is undoable; history survives archiving
- **ntfy and webhook notifications, archive retention:** one ping per filing batch; applied entries archive after `ARCHIVE_AFTER_DAYS`

## How it works

| stage | who | what happens |
|---|---|---|
| file | agent | `file_entries` through the stdio shim, which finds or spawns one shared daemon |
| notify | daemon | pings ntfy or the webhook with a dashboard link |
| author | human | edits, approves, or rejects on the dashboard; saving prose releases it as `approved` |
| fetch | agent | `fetch_approved` (or a blocking `await_approved`) returns text, anchor, constraints, and the merged style guide |
| apply | agent | replaces the anchor, reports through `mark_applied` |
| archive | daemon | applied entries past `ARCHIVE_AFTER_DAYS` move to the archive with their revisions |

## Install

```sh
bunx reviewzy@latest
```

runs the published npm version (the default). to switch to the latest git version:

```sh
bunx github:uwuclxdy/reviewzy
```

pin a commit with `bunx github:uwuclxdy/reviewzy#<sha>` to keep it stable.

requires bun >= 1.3 (the store is `bun:sqlite`; npm/npx not supported). the shim spawns one shared daemon per machine on first use.

from a checkout:

```sh
bun install
bun run daemon
```

### Claude Code plugin

the plugin registers the MCP server and adds a `/reviewzy` slash command plus the reviewzy skill.

```
/plugin marketplace add uwuclxdy/reviewzy
/plugin install reviewzy@reviewzy
```

#### run a checkout as the plugin (dev)

the marketplace above pins the published version. to run your local checkout instead, first repoint the plugin's mcp server at the linked binary: in `.claude-plugin/plugin.json`, change `mcpServers.reviewzy.command` from `bunx` to `reviewzy` and drop `args`. keep that edit uncommitted. the committed value stays `bunx reviewzy@latest` for publish. set `REVIEWZY_DEV=1` in `~/.claude/settings.json`'s `env` (or export it in your shell) so the shim drains and respawns the daemon from the checkout on every session start. then, from the checkout root:

```sh
bun link                              # reviewzy on PATH, symlinked to bin/reviewzy
claude plugin marketplace add ./      # register the checkout as a marketplace
claude plugin install reviewzy@reviewzy
```

without `REVIEWZY_DEV=1`, the shared daemon is keyed on version: a same-version daemon already running from npm keeps serving, so `src/` edits never reach it. bumping `version` or killing the daemon is the manual fallback.

## Usage

```sh
curl http://127.0.0.1:3123/health
```

```json
{"name":"reviewzy","version":"0.2.0","pid":4172598,"nonce":"881f0e93-8dde-4c75-b559-d2e3d3b223f0","startedAt":"2026-08-16T19:31:28.640Z"}
```

open http://127.0.0.1:3123/ for the dashboard.

## Configuration

copy `.env.example` to `.env` and edit. every key is optional; a bad value refuses startup.

| key | default | what it does |
|---|---|---|
| `REVIEWZY_PORT` | `3123` | dashboard and mcp port; the bind is `127.0.0.1` always |
| `REVIEWZY_DB` | `~/.local/share/reviewzy/reviewzy.db` | sqlite file (`$XDG_DATA_HOME` respected) |
| `REVIEWZY_BASE_URL` | `http://127.0.0.1:$REVIEWZY_PORT` | dashboard url used in notification links |
| `REVIEWZY_DEV` | unset | set to `1` to drain and respawn the daemon from the checkout on every session start (dev only) |
| `REVIEWZY_TOKEN` | unset | bearer token guarding mcp; set it and it is required |
| `DASHBOARD_PASSWORD` | unset | dashboard login; set it and it is required |
| `ARCHIVE_AFTER_DAYS` | `90` | days an applied entry waits before archiving |
| `NTFY_URL` + `NTFY_TOPIC` | unset | ntfy server and topic; ntfy fires only when both are set |
| `NTFY_PRIORITY` | `3` | ntfy priority, `1` to `5` |
| `WEBHOOK_URL` | unset | generic webhook; posts project, batch id, count, and a dashboard link |

a credential left unset keeps that surface loopback-only with a startup warning.

## Integrations

point an mcp client at reviewzy to start filing:

```json
{
  "mcpServers": {
    "reviewzy": {
      "command": "bunx",
      "args": ["reviewzy@latest"]
    }
  }
}
```

the endpoint speaks the stateless `2026-07-28` mcp revision.

## Comparison

| | reviewzy | Contentrain | gotoHuman | Ditto |
|---|---|---|---|---|
| authorship | human authors, agent files and applies | agent authors, human approves | human edits form fields | team edits a live library |
| code integration | anchors on live strings, no externalization | restructures the repo into a content layer | generic forms, no write-back | no code patch-back |
| hosting | self-hosted | self-hosted | saas only | saas only |
| queue | async queue plus blocking long-poll | queue, git/PR-first review | async queue | none |

the blocking human-in-the-loop mcp family pops a dialog per call: no queue, no persistence, no dashboard.

## FAQ

**How do I approve AI-generated text before it lands in my code?** the agent files text as drafts, you author or approve them on the dashboard, the agent applies.

**Can an AI agent rewrite my CLI help text without i18n keys?** yes. entries anchor on the exact current text; apply-back is a targeted replace.

**How do I bulk-rewrite all user-facing copy in a codebase?** file batches per project, group by project or batch on the dashboard, approve in bulk with a before/after diff, then let a later agent session fetch and apply.

**How do I update the style guide my agents read?** edit it on the dashboard at `/style-guide`. a saved edit reaches agents in up to 60 seconds.

## Development

```sh
bun run check  # tsc --noEmit && bun test
```

## License

[AGPL-3.0](LICENSE). self-hosted stays self-hosted; a saas wrapping it publishes its changes.