@satanwagen/reviewkit 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +158 -0
- package/README.md +158 -64
- package/dist/activate.cjs +150 -0
- package/dist/activate.cjs.map +1 -0
- package/dist/activate.d.cts +59 -0
- package/dist/activate.d.ts +59 -0
- package/dist/activate.js +21 -0
- package/dist/activate.js.map +1 -0
- package/dist/{chunk-YWBFAV57.js → chunk-5MAFDRUI.js} +1161 -1555
- package/dist/chunk-5MAFDRUI.js.map +1 -0
- package/dist/{chunk-4YNLMSCK.js → chunk-DG6O5XIC.js} +36 -10
- package/dist/chunk-DG6O5XIC.js.map +1 -0
- package/dist/chunk-EOX3UJNP.js +36 -0
- package/dist/chunk-EOX3UJNP.js.map +1 -0
- package/dist/chunk-ETAGGIDN.js +119 -0
- package/dist/chunk-ETAGGIDN.js.map +1 -0
- package/dist/client/index.cjs +4834 -4244
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +49 -3
- package/dist/client/index.js.map +1 -1
- package/dist/effects-EWKHKUDP.js +851 -0
- package/dist/effects-EWKHKUDP.js.map +1 -0
- package/dist/index-CWGkg1-E.d.cts +86 -0
- package/dist/index-DPsjkvaY.d.ts +86 -0
- package/dist/index.cjs +4852 -4267
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +35 -3
- package/dist/index.js.map +1 -1
- package/dist/lazy.cjs +9308 -0
- package/dist/lazy.cjs.map +1 -0
- package/dist/lazy.d.cts +7 -0
- package/dist/lazy.d.ts +7 -0
- package/dist/lazy.js +8 -0
- package/dist/lazy.js.map +1 -0
- package/dist/next.cjs +9309 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +4 -0
- package/dist/next.d.ts +4 -0
- package/dist/next.js +9 -0
- package/dist/next.js.map +1 -0
- package/dist/schema.cjs +53 -16
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +20 -5
- package/dist/schema.d.ts +20 -5
- package/dist/schema.js +30 -2
- package/dist/schema.js.map +1 -1
- package/package.json +23 -8
- package/cli/emblema-sync.mjs +0 -675
- package/dist/chunk-4YNLMSCK.js.map +0 -1
- package/dist/chunk-YWBFAV57.js.map +0 -1
- package/dist/index-BbucFgZi.d.ts +0 -49
- package/dist/index-CBlJrKkm.d.cts +0 -49
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.4 — 2026-09-02
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`reviewkit` terminal CLI** (in `@satanwagen/reviewkit-cli`, zero deps,
|
|
8
|
+
Node 21+): `cd repo && reviewkit` opens an interactive live view of every
|
|
9
|
+
review item across the site's pages: presence (who is online, in their
|
|
10
|
+
colours), arrow-key navigation, Enter for actions (detail, apply with
|
|
11
|
+
Claude Code, copy prompt, set status, delete), `a` to hand all open items
|
|
12
|
+
to Claude Code, filters, live toasts for new / changed / deleted items.
|
|
13
|
+
Piped output falls back to a plain table plus event stream. `list`, `show`,
|
|
14
|
+
`status`, `delete`, `watch`, `export`, `prompt`, `rooms`, `projects`, `key`.
|
|
15
|
+
Project resolution: `-p name` → `.reviewkit/project.json` (walks up from
|
|
16
|
+
cwd) → `.reviewkit/emblema-sync.json` → global active project.
|
|
17
|
+
- **One-click pairing.** `reviewkit setup` detects the site's sync settings in
|
|
18
|
+
the repo, listens on loopback (127.0.0.1:48780–48783) and the review panel's
|
|
19
|
+
new **Connect terminal / agent…** dialog finds it and pushes the connection.
|
|
20
|
+
Nothing to copy. A copyable `rkc_…` key remains for remote machines
|
|
21
|
+
(`reviewkit connect <key>`). CSP: the panel needs `connect-src
|
|
22
|
+
http://127.0.0.1:*` for this (already required by the Emblema bridge).
|
|
23
|
+
- **MCP bridge for coding agents.** `reviewkit mcp` is a stdio MCP server
|
|
24
|
+
with `reviewkit_prompt`, `reviewkit_list`, `reviewkit_show`,
|
|
25
|
+
`reviewkit_set_status`, `reviewkit_delete`, `reviewkit_export`,
|
|
26
|
+
`reviewkit_rooms`, `reviewkit_projects`. `setup` / `mcp-install` register it
|
|
27
|
+
in Claude Code (`claude mcp add`) and Cursor (`~/.cursor/mcp.json`).
|
|
28
|
+
Statuses set by the agent appear live in reviewers' browsers.
|
|
29
|
+
- **Sync server: `rooms` request** (protocol v1, additive). Returns the routes
|
|
30
|
+
of the connection's origin that have review data, live or on disk, with item
|
|
31
|
+
counts and online peers. Needed by the CLI. Deployed on the satanwagen
|
|
32
|
+
server, reachable again at `wss://2-59-219-26.sslip.io/rk-sync` (sslip.io
|
|
33
|
+
hostname, no domain to expire) — and the client's default `syncUrl` again.
|
|
34
|
+
- Panel gear menu: **Connect terminal / agent…** (enabled when live sync is on).
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- **One word does everything: `<ReviewKit token="…" />`.** Live collaboration
|
|
39
|
+
is on by default: `syncUrl` defaults to the public ReviewKit server
|
|
40
|
+
`wss://2-59-219-26.sslip.io/rk-sync` (sslip.io hostname → VPS IP, nothing to
|
|
41
|
+
expire) and `syncToken` defaults to `token`. The server is now
|
|
42
|
+
**self-serve**: any token of at least 8 characters gets its own private set
|
|
43
|
+
of rooms (keyed by origin + route + hash(token)), so no account and no
|
|
44
|
+
server-side step exist. New prop `sync={false}` forces solo mode;
|
|
45
|
+
`syncToken` stays as an override when the room secret should differ from
|
|
46
|
+
the review link. Existing installs that set `token` but no `syncToken`
|
|
47
|
+
start syncing on upgrade — pass `sync={false}` to keep the old solo mode.
|
|
48
|
+
Same in the CLI: `init --sync-url` is optional, state files still carrying
|
|
49
|
+
the retired host fall back to the default with a warning, and `reviewkit
|
|
50
|
+
setup` picks up the review token from the repo as the room secret.
|
|
51
|
+
- `@satanwagen/reviewkit-cli` now ships two tools. `npx -y
|
|
52
|
+
@satanwagen/reviewkit-cli …` runs `reviewkit`; the Emblema agent is
|
|
53
|
+
`npx -p @satanwagen/reviewkit-cli reviewkit-emblema …` (docs and in-app
|
|
54
|
+
hints updated).
|
|
55
|
+
|
|
56
|
+
## 0.1.3 — 2026-09-02
|
|
57
|
+
|
|
58
|
+
Defaults tightened so a production build behaves the way its code reads.
|
|
59
|
+
Several changes are breaking for hosts that relied on the old conveniences;
|
|
60
|
+
each has a one-line migration below.
|
|
61
|
+
|
|
62
|
+
### Breaking
|
|
63
|
+
|
|
64
|
+
- **No activation without a token.** Previously an unset `token` meant "any
|
|
65
|
+
`?review=x` turns the layer on" (plus the `reviewkit` localStorage flag).
|
|
66
|
+
Now, without `token`, nothing activates. Migration: pass `token`. For a
|
|
67
|
+
reviewer-less dev loop pass `devAutoOn`.
|
|
68
|
+
- **No `NODE_ENV === 'development'` auto-on.** The library never reads the
|
|
69
|
+
host's NODE_ENV any more. Migration:
|
|
70
|
+
`devAutoOn={import.meta.env.DEV}` (Vite) or
|
|
71
|
+
`devAutoOn={process.env.NODE_ENV === 'development'}` (Next/webpack).
|
|
72
|
+
- **No default `syncUrl`.** The former default
|
|
73
|
+
`wss://agropolio.fucking.style/rk-sync` pointed at an expired domain. Sync
|
|
74
|
+
now runs only when **both** `syncUrl` and `syncToken` are set; otherwise
|
|
75
|
+
the layer is local-only, no socket. Migration: set `syncUrl` explicitly.
|
|
76
|
+
Same for the CLI: `reviewkit-emblema init --sync-url …` (the room bridge
|
|
77
|
+
stays disabled until both `--sync-url` and `--sync-token` are configured).
|
|
78
|
+
- **CLI moved to `@satanwagen/reviewkit-cli`.** The main package no longer
|
|
79
|
+
ships a `bin`, so a host `npm install` does not pull the Emblema agent.
|
|
80
|
+
Migration: `npx -p @satanwagen/reviewkit-cli reviewkit-emblema init …` / `serve` (the command
|
|
81
|
+
inside is still `reviewkit-emblema`).
|
|
82
|
+
- **`emblema.repoPath` is optional.** The repo path is Emblema's local
|
|
83
|
+
knowledge (advertised in its `/intake/ping` answer, and present in
|
|
84
|
+
`.reviewkit/emblema-sync.json` for the agent) and no longer belongs in host
|
|
85
|
+
code. `emblema={{}}` now enables the bridge. The v1 dispatch payload keeps
|
|
86
|
+
`v: 1`, but `repoPath` became optional: an intake with a strict "required"
|
|
87
|
+
validator must relax it and fall back to its active project.
|
|
88
|
+
- **Effect sounds moved off the expired domain.** `/ondro` now fetches
|
|
89
|
+
`rick.mp3` from `https://2-59-219-26.sslip.io/rk-assets` (the same VPS,
|
|
90
|
+
reached through a sslip.io hostname so no domain renewal can break it).
|
|
91
|
+
New prop `fxAssetsUrl` overrides the base for self-hosting. Add the origin
|
|
92
|
+
to CSP `media-src` if you send a policy.
|
|
93
|
+
- Legacy activation keys (`review-kit:active`, `reviewkit`) are still cleaned
|
|
94
|
+
up on load but never consulted.
|
|
95
|
+
- **Emblema intake must accept a batch without `repoPath`.** An Emblema build
|
|
96
|
+
that still validates it as required answers HTTP 400; the panel now shows
|
|
97
|
+
that as `emblema rejected` (new push status) instead of `emblema offline`.
|
|
98
|
+
- **CLI:** a `.reviewkit/emblema-sync.json` written by 0.1.2 still carries the
|
|
99
|
+
retired default sync host. `serve` now detects it, logs a re-init hint and
|
|
100
|
+
runs with the bridge disabled instead of reconnecting forever; the local
|
|
101
|
+
`/status` endpoint reports `realtime.state: 'off'` whenever either sync
|
|
102
|
+
setting is missing (previously it said `connecting` with no URL).
|
|
103
|
+
|
|
104
|
+
### Added
|
|
105
|
+
|
|
106
|
+
- `enabled?: boolean` master switch (default `true`) so a wrapper can write
|
|
107
|
+
`<ReviewKit enabled={Boolean(TOKEN)} token={TOKEN} />` instead of
|
|
108
|
+
`if (!TOKEN) return null`.
|
|
109
|
+
- `devAutoOn?: boolean` — host-controlled "always on for this load". Still
|
|
110
|
+
subject to `enabled`, `allowedHosts` and `?review=off`.
|
|
111
|
+
- **`@satanwagen/reviewkit/next`** — `'use client'` component: render it from
|
|
112
|
+
a server `layout.tsx`; it evaluates URL + sticky session after mount and
|
|
113
|
+
only then dynamically imports the core. `examples/next-demo` shows it
|
|
114
|
+
together with the CSP headers.
|
|
115
|
+
- **`@satanwagen/reviewkit/lazy`** — the same code-split component for Vite
|
|
116
|
+
and friends; no more hand-rolled `React.lazy` + `Suspense` gate.
|
|
117
|
+
- **`@satanwagen/reviewkit/activate`** — `shouldActivate({ token, enabled,
|
|
118
|
+
allowedHosts, devAutoOn })`, React-free, ~200 B. The supported way for a
|
|
119
|
+
host to gate its own lazy import; the sessionStorage format stays private.
|
|
120
|
+
- One-time console warning when a URL token activates the layer on a
|
|
121
|
+
non-loopback hostname with no `allowedHosts` (see README "Production
|
|
122
|
+
recipe").
|
|
123
|
+
- The `/` command effects (~40 KB of source) load as a separate chunk on
|
|
124
|
+
first use instead of shipping in the core chunk.
|
|
125
|
+
- `probeEmblema()` returns `{ port, repoPath? }`; `pingEmblema()` stays as a
|
|
126
|
+
port-only wrapper.
|
|
127
|
+
- Activation matrix tests (`test/activate.test.mjs`) run against `dist/`
|
|
128
|
+
(`npm test` builds first via `pretest`).
|
|
129
|
+
- A one-time console warning when `?review=<x>` does not match the configured
|
|
130
|
+
token (kept from 0.1.2, now also emitted by `shouldActivate`).
|
|
131
|
+
- `/lazy` and `/next` are effectively ESM entries: the CJS builds exist for
|
|
132
|
+
`require()` consumers but inline the core (esbuild has no CJS code
|
|
133
|
+
splitting). Every modern bundler picks the ESM build.
|
|
134
|
+
- README: activation-state table, CSP requirements, production recipe.
|
|
135
|
+
|
|
136
|
+
### Bundle audit (unminified ESM, `dist/`)
|
|
137
|
+
|
|
138
|
+
| Chunk | 0.1.2 | 0.1.3 |
|
|
139
|
+
| --- | ---: | ---: |
|
|
140
|
+
| core client chunk | 313 KB | 267 KB |
|
|
141
|
+
| effects (lazy, on first `/command`) | in core | 41 KB |
|
|
142
|
+
| `activate` entry | — | 0.2 KB |
|
|
143
|
+
|
|
144
|
+
Largest remaining inputs: `styles.ts` (53 KB), `Panel.tsx` (46 KB). Schema
|
|
145
|
+
validation and IndexedDB persistence are under 10 KB each and were not the
|
|
146
|
+
weight.
|
|
147
|
+
|
|
148
|
+
## 0.1.2
|
|
149
|
+
|
|
150
|
+
- Delete endpoint for Emblema findings, idempotent for unknown ids.
|
|
151
|
+
|
|
152
|
+
## 0.1.1
|
|
153
|
+
|
|
154
|
+
- `reopen` status verb; realtime health in local state.
|
|
155
|
+
|
|
156
|
+
## 0.1.0
|
|
157
|
+
|
|
158
|
+
- First scoped release as `@satanwagen/reviewkit`.
|
package/README.md
CHANGED
|
@@ -13,35 +13,104 @@ npm install @satanwagen/reviewkit
|
|
|
13
13
|
## Two-line integration
|
|
14
14
|
|
|
15
15
|
```tsx
|
|
16
|
-
import { ReviewKit } from '@satanwagen/reviewkit/
|
|
16
|
+
import { ReviewKit } from '@satanwagen/reviewkit/lazy'; // Vite, CRA, Remix …
|
|
17
|
+
// import { ReviewKit } from '@satanwagen/reviewkit/next'; // Next.js App Router ('use client' built in)
|
|
17
18
|
|
|
18
19
|
// anywhere in your tree, rendered once:
|
|
19
|
-
<ReviewKit />
|
|
20
|
+
<ReviewKit token="pick-any-word" />
|
|
20
21
|
```
|
|
21
22
|
|
|
22
|
-
That's it. The component renders `null` unless review mode is active, and all UI lives in a shadow root appended to `document.body` — your CSS never leaks in, reviewkit's CSS never leaks out. Safe to ship in production builds.
|
|
23
|
+
That's it — one word. Reviewers open `https://your-site/?review=pick-any-word`; the same word is also the secret of your private rooms on the public ReviewKit sync server, so presence, live cursors, shared items, the terminal CLI and the Claude Code bridge all work with no server-side step. The component renders `null` unless review mode is active, and all UI lives in a shadow root appended to `document.body` — your CSS never leaks in, reviewkit's CSS never leaks out. Safe to ship in production builds. Want it offline? `sync={false}`.
|
|
24
|
+
|
|
25
|
+
Three entries, same props:
|
|
26
|
+
|
|
27
|
+
| Entry | What it is | Use when |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| `@satanwagen/reviewkit/lazy` | Component that decides after mount (URL + sticky session) and only then `import()`s the core chunk. | Vite / CRA / Remix / any client bundler. Visitors without a review link never download the core. |
|
|
30
|
+
| `@satanwagen/reviewkit/next` | Same, with `'use client'` baked into the built file so a server `layout.tsx` can render it directly. | Next.js App Router. See `examples/next-demo`. |
|
|
31
|
+
| `@satanwagen/reviewkit/client` | The core itself, eager. | You already have your own lazy boundary, or you want everything in one chunk. |
|
|
32
|
+
|
|
33
|
+
The code split only exists in the ESM builds (what Vite, Next, webpack 5 pick); the CJS files of `/lazy` and `/next` inline the core for `require()` consumers.
|
|
34
|
+
|
|
35
|
+
And a React-free helper for hosts that gate their own lazy import:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { shouldActivate } from '@satanwagen/reviewkit/activate'; // ~200 B, no React, SSR-safe (false)
|
|
39
|
+
if (shouldActivate({ token: TOKEN })) void import('./reviewHost');
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`shouldActivate` is the supported API for "should this page load show review?"; the sessionStorage format behind the sticky session is private and may change. Its only side effect: `?review=off` clears the sticky opt-in.
|
|
23
43
|
|
|
24
44
|
### Props
|
|
25
45
|
|
|
26
46
|
| Prop | Type | Description |
|
|
27
47
|
| --- | --- | --- |
|
|
28
|
-
| `token` | `string` |
|
|
29
|
-
| `
|
|
48
|
+
| `token` | `string` | **Required for any activation.** The layer turns on only via `?review=<token>` (or the per-tab session such a link starts). Without it nothing activates, whatever the URL says. |
|
|
49
|
+
| `enabled` | `boolean` | Master switch, default `true`. `false` renders nothing, installs nothing. Lets you write `enabled={Boolean(TOKEN)}` instead of `if (!TOKEN) return null`. |
|
|
50
|
+
| `devAutoOn` | `boolean` | Force the layer on for this page load, no link needed. Host-controlled dev convenience (`devAutoOn={import.meta.env.DEV}`); the library never reads `NODE_ENV`. Still subject to `enabled`, `allowedHosts` and `?review=off`. |
|
|
51
|
+
| `allowedHosts` | `string[]` | Hostname allowlist with `*` wildcards (`['localhost', '*.staging.example']`). Outside it nothing activates, token or not. Unset = any host (with a one-time console warning on URL activation on a public host). |
|
|
52
|
+
| `syncUrl` | `string` | WebSocket endpoint of the sync server. Default: the public ReviewKit server `wss://2-59-219-26.sslip.io/rk-sync`, so live collaboration needs no server of your own. Override for a self-hosted `realtime/server`. |
|
|
53
|
+
| `sync` | `boolean` | Live collaboration switch, default `true`. `false` = solo mode, no network. |
|
|
54
|
+
| `syncToken` | `string` | Secret of your rooms on the sync server, **defaults to `token`**. Any string of 8+ characters gets its own private rooms on the public server. Override only if the room secret should differ from the review link. Public in the bundle by nature. |
|
|
55
|
+
| `emblema` | `{ repoPath?: string; port?: number }` | Enables the Emblema bridge (`emblema={{}}` is enough). See "Pairing with Emblema". |
|
|
30
56
|
| `defaultAuthor` | `string` | Pre-fills the reviewer name. |
|
|
57
|
+
| `fxAssetsUrl` | `string` | Base URL for the `/` effect soundtracks (`<base>/rick.mp3` for `/ondro`). Default: the public ReviewKit assets host `https://2-59-219-26.sslip.io/rk-assets`. Override to self-host. Needs `media-src` in CSP. |
|
|
31
58
|
| `position` | `'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right'` | Initial corner of the floating badge (default `bottom-right`; draggable, persisted). |
|
|
32
59
|
| `storageKey` | `string` | Persistence key (default: page origin). |
|
|
33
60
|
| `onSessionChange` | `(session: ReviewSession) => void` | Called (debounced) whenever the session changes. |
|
|
34
61
|
|
|
35
62
|
## How the layer activates
|
|
36
63
|
|
|
37
|
-
|
|
64
|
+
Rules, in order; the first that applies wins. No environment sniffing anywhere.
|
|
65
|
+
|
|
66
|
+
| # | Condition | Result |
|
|
67
|
+
| --- | --- | --- |
|
|
68
|
+
| 1 | `enabled={false}` | off |
|
|
69
|
+
| 2 | hostname not in `allowedHosts` (when given) | off |
|
|
70
|
+
| 3 | `?review=off` in the URL | off, and the in-tab sticky session is cleared |
|
|
71
|
+
| 4 | `devAutoOn` | **on** |
|
|
72
|
+
| 5 | no `token` configured | off (`?review=x` logs a one-time warning and stays off) |
|
|
73
|
+
| 6 | `?review=<token>` matches | **on**, starts the per-tab sticky session |
|
|
74
|
+
| 7 | `?review=<something else>` | off |
|
|
75
|
+
| 8 | valid sticky session (same token, < 4 h since last active load) | **on**, expiry renewed |
|
|
76
|
+
| 9 | otherwise | off |
|
|
77
|
+
|
|
78
|
+
Activation-state matrix for the common cases:
|
|
79
|
+
|
|
80
|
+
| token | build | URL | sticky | result |
|
|
81
|
+
| --- | --- | --- | --- | --- |
|
|
82
|
+
| unset | dev or prod | none / `?review=x` | any | off |
|
|
83
|
+
| unset | dev, with `devAutoOn` | any except `off` | any | on |
|
|
84
|
+
| set | prod | `?review=<token>` | — | on (sticky starts) |
|
|
85
|
+
| set | prod | none | valid | on |
|
|
86
|
+
| set | prod | none | none / expired / other token | off |
|
|
87
|
+
| set | prod | `?review=wrong` | valid | off |
|
|
88
|
+
| set | any | `?review=off` | valid | off, sticky cleared |
|
|
89
|
+
|
|
90
|
+
The sticky session is per tab (`sessionStorage`), bound to the token and renewed on every active load, so a reviewer who opened a review link keeps reviewing across full page loads on other routes. The gear menu's **Keep review on in this tab** toggles it explicitly; **End review** (or `?review=off`) clears everything and returns the clean site. Leftover keys from pre-0.1.3 versions (`review-kit:active`, `reviewkit`) are removed on load and never consulted.
|
|
91
|
+
|
|
92
|
+
### Production recipe
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
<ReviewKit
|
|
96
|
+
enabled={Boolean(TOKEN)}
|
|
97
|
+
token={TOKEN}
|
|
98
|
+
allowedHosts={['staging.your-site.tld', '*.preview.your-site.tld']}
|
|
99
|
+
/>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`token` + `allowedHosts` together: the token says *who*, the allowlist says *where*. Without `allowedHosts` a URL activation on a non-loopback host logs one console warning. `TOKEN` is public in the bundle by design; use the `/lazy` or `/next` entry so it at least lives in the review chunk, not the main one.
|
|
103
|
+
|
|
104
|
+
### CSP
|
|
105
|
+
|
|
106
|
+
If the host sends a `Content-Security-Policy`, `connect-src` must allow:
|
|
38
107
|
|
|
39
|
-
|
|
40
|
-
|
|
108
|
+
- the sync server origin: `https://2-59-219-26.sslip.io` for the default server (or your own `syncUrl` origin / `ws:` in dev),
|
|
109
|
+
- `http://127.0.0.1:*` when the `emblema` prop is set (intake ports 48752–48755, pairing agent 48770–48773).
|
|
41
110
|
|
|
42
|
-
|
|
111
|
+
and `media-src` must allow `https://2-59-219-26.sslip.io` (or your `fxAssetsUrl`) for the `/` effect sounds; without it the effects run silent.
|
|
43
112
|
|
|
44
|
-
|
|
113
|
+
With `connect-src 'self'` alone the WebSocket is blocked and the layer silently stays local-only. `examples/next-demo/next.config.ts` has a complete header.
|
|
45
114
|
|
|
46
115
|
## Using it (reviewer flow)
|
|
47
116
|
|
|
@@ -121,70 +190,64 @@ Programmatic helpers are exported too: `parseSessionJson` / `parseSession` (vali
|
|
|
121
190
|
// src/main.tsx
|
|
122
191
|
import { StrictMode } from 'react';
|
|
123
192
|
import { createRoot } from 'react-dom/client';
|
|
124
|
-
import { ReviewKit } from '@satanwagen/reviewkit/
|
|
193
|
+
import { ReviewKit } from '@satanwagen/reviewkit/lazy';
|
|
125
194
|
import { App } from './App';
|
|
126
195
|
|
|
127
196
|
createRoot(document.getElementById('root')!).render(
|
|
128
197
|
<StrictMode>
|
|
129
198
|
<App />
|
|
130
|
-
<ReviewKit token={import.meta.env.VITE_REVIEW_TOKEN} />
|
|
199
|
+
<ReviewKit token={import.meta.env.VITE_REVIEW_TOKEN} devAutoOn={import.meta.env.DEV} />
|
|
131
200
|
</StrictMode>,
|
|
132
201
|
);
|
|
133
202
|
```
|
|
134
203
|
|
|
135
|
-
No Vite config changes are needed — `reviewkit` ships prebuilt ESM/CJS with types. In dev the layer is
|
|
204
|
+
No Vite config changes are needed — `reviewkit` ships prebuilt ESM/CJS with types. In `vite dev` the layer is on thanks to `devAutoOn`; in production reviewers open `https://your-site.tld/?review=<token>`, and visitors without the link never download the review chunk (the `/lazy` entry splits it automatically).
|
|
136
205
|
|
|
137
|
-
Until the npm release lands you can also depend on it via a file path — this is exactly what the
|
|
206
|
+
Until the npm release lands you can also depend on it via a file path — this is exactly what the demos do:
|
|
138
207
|
|
|
139
208
|
```jsonc
|
|
140
209
|
// package.json
|
|
141
210
|
"dependencies": { "@satanwagen/reviewkit": "file:../path/to/ReviewKit" }
|
|
142
211
|
```
|
|
143
212
|
|
|
144
|
-
npm symlinks the folder, so after editing reviewkit run `npm run build` in it and restart/reload Vite.
|
|
213
|
+
npm symlinks the folder, so after editing reviewkit run `npm run build` in it and restart/reload Vite. With React 18 add `resolve: { dedupe: ['react', 'react-dom'] }` to `vite.config.ts` so the linked package's own `node_modules` cannot introduce a second React. Run `npm run build` in `ReviewKit/` at least once before installing, since the package resolves to `dist/`.
|
|
145
214
|
|
|
146
|
-
###
|
|
215
|
+
### Gating a hand-rolled lazy boundary
|
|
147
216
|
|
|
148
|
-
|
|
217
|
+
If you keep your own wrapper (for example to keep `syncToken` in a chunk you control), gate it with `shouldActivate` instead of reading ReviewKit's storage keys:
|
|
149
218
|
|
|
150
|
-
```
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
219
|
+
```tsx
|
|
220
|
+
// ReviewLayer.tsx
|
|
221
|
+
import { lazy, Suspense, useEffect, useState } from 'react';
|
|
222
|
+
import { shouldActivate } from '@satanwagen/reviewkit/activate';
|
|
154
223
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
// dedupe guarantees a single React instance (Fable is on React 18):
|
|
158
|
-
resolve: { dedupe: ['react', 'react-dom'] }
|
|
159
|
-
```
|
|
224
|
+
const TOKEN = import.meta.env.VITE_REVIEW_TOKEN;
|
|
225
|
+
const ReviewHost = lazy(() => import('./reviewHost')); // renders <ReviewKit token={TOKEN} syncUrl=… syncToken=… />
|
|
160
226
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const TOKEN = 'agropolio'
|
|
166
|
-
const SESSION_KEY = 'review-kit:active' // same key reviewkit uses internally
|
|
167
|
-
|
|
168
|
-
function shouldLoad(): boolean {
|
|
169
|
-
try {
|
|
170
|
-
const param = new URLSearchParams(window.location.search).get('review')
|
|
171
|
-
if (param === 'off') { window.sessionStorage.removeItem(SESSION_KEY); return false }
|
|
172
|
-
if (param === TOKEN) return true
|
|
173
|
-
return window.sessionStorage.getItem(SESSION_KEY) === '1' // survive reloads in-tab
|
|
174
|
-
} catch { return false }
|
|
227
|
+
export function ReviewLayer() {
|
|
228
|
+
const [load, setLoad] = useState(false);
|
|
229
|
+
useEffect(() => { if (shouldActivate({ token: TOKEN })) setLoad(true); }, []);
|
|
230
|
+
return load ? <Suspense fallback={null}><ReviewHost /></Suspense> : null;
|
|
175
231
|
}
|
|
232
|
+
```
|
|
176
233
|
|
|
177
|
-
|
|
234
|
+
## Integrating into a Next.js site (App Router)
|
|
178
235
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
236
|
+
```tsx
|
|
237
|
+
// app/layout.tsx (a server component — that's fine)
|
|
238
|
+
import { ReviewKit } from '@satanwagen/reviewkit/next';
|
|
239
|
+
|
|
240
|
+
export default function RootLayout({ children }) {
|
|
241
|
+
return (
|
|
242
|
+
<html><body>
|
|
243
|
+
{children}
|
|
244
|
+
<ReviewKit token={process.env.NEXT_PUBLIC_REVIEW_TOKEN} devAutoOn={process.env.NODE_ENV === 'development'} />
|
|
245
|
+
</body></html>
|
|
246
|
+
);
|
|
184
247
|
}
|
|
185
248
|
```
|
|
186
249
|
|
|
187
|
-
The
|
|
250
|
+
The `/next` entry is a client component; it renders nothing on the server and evaluates the URL/sticky session after mount before importing the core. Add the CSP `connect-src` entries above if you send a policy. Full example: `examples/next-demo`.
|
|
188
251
|
|
|
189
252
|
## Adding ReviewKit to another site (with live collaboration)
|
|
190
253
|
|
|
@@ -203,19 +266,19 @@ Step by step, for any site on any hosting (nginx, IIS, Vercel, …).
|
|
|
203
266
|
|
|
204
267
|
Run `npm run build` inside ReviewKit at least once for the `file:`/git variants (the package resolves to `dist/`). With React 18 add `resolve.dedupe: ['react','react-dom']` to your bundler config when using a `file:` link.
|
|
205
268
|
|
|
206
|
-
**2. Integrate —
|
|
269
|
+
**2. Integrate — one line, done:**
|
|
207
270
|
|
|
208
271
|
```tsx
|
|
209
|
-
import { ReviewKit } from '@satanwagen/reviewkit/
|
|
272
|
+
import { ReviewKit } from '@satanwagen/reviewkit/lazy';
|
|
210
273
|
|
|
211
|
-
<ReviewKit token="my-site-token"
|
|
274
|
+
<ReviewKit token="my-site-token" />
|
|
212
275
|
```
|
|
213
276
|
|
|
214
|
-
|
|
277
|
+
Live collaboration (presence, live cursors, instantly shared items) is on by default: `syncUrl` points at the public ReviewKit server `wss://2-59-219-26.sslip.io/rk-sync` and the room secret is your `token` (8+ characters; `syncToken` overrides it). The server is self-serve: every token gets its own private rooms, no account, no registration. Rooms are keyed by the page's origin + path, so different sites never see each other's feedback or presence. If the server is unreachable the tool silently runs local-only (no console errors); omit either prop to disable networking entirely.
|
|
215
278
|
|
|
216
|
-
**What
|
|
279
|
+
**What a shared server implies (honesty section):** the token is readable in the browser bundle and in every review link, so anyone holding the link can join your rooms — that is the point of a review link. Rooms of different tokens never mix. The public server is guarded by per-connection rate limits (40 msg/s, burst 80), 1 MiB frame cap, 16 clients and 2000 items per room, and a live-room ceiling (`RK_SYNC_MAX_ROOMS`, default 500).
|
|
217
280
|
|
|
218
|
-
**3.
|
|
281
|
+
**3. Run the sync server** — `realtime/server/`: `npm run build`, run `dist/server.js` with `RK_SYNC_TOKEN` (+ optional `RK_SYNC_PORT`, `RK_SYNC_DATA`, `RK_SYNC_MAX_ROOMS`, and `RK_SYNC_ORIGINS` if you *want* an origin allowlist — empty/unset means any origin, which is the default deployment) as a service, point `syncUrl` at it, then put a WebSocket-aware reverse proxy in front:
|
|
219
282
|
|
|
220
283
|
*nginx* (as deployed on satan):
|
|
221
284
|
|
|
@@ -233,23 +296,49 @@ That's the whole setup. `syncUrl` **defaults to the always-on ReviewKit sync ser
|
|
|
233
296
|
|
|
234
297
|
With an `RK_SYNC_ORIGINS` allowlist active, rejected origins are logged (`[origin] rejected …`) and closed with code 4005; the client quietly stays local.
|
|
235
298
|
|
|
236
|
-
**4. Token caveat:** don't reuse secrets that protect anything else, and keep tokens out of the main bundle (
|
|
299
|
+
**4. Token caveat:** don't reuse secrets that protect anything else, and keep tokens out of the main bundle (use the `/lazy` or `/next` entry). Add `allowedHosts` in production (see "Production recipe").
|
|
237
300
|
|
|
238
301
|
**5. Verify it works:** open `https://your-site/?review=<token>` — the Review badge appears; the panel header shows **live** (green dot) when the sync server accepted you, or *local only* when not. In the Network tab you should see one WebSocket to `/rk-sync` with a `welcome` frame. Open the same URL in a second browser: avatars, live cursors and instantly-appearing items confirm collaboration.
|
|
239
302
|
|
|
303
|
+
## Terminal & coding agents (`reviewkit` CLI)
|
|
304
|
+
|
|
305
|
+
Review comments do not have to stay in the browser. The `reviewkit` CLI reads the same live rooms, and doubles as the MCP bridge for Claude Code, Cursor and any other MCP client, so an agent can read every comment and mark items applied while reviewers watch the status flip live.
|
|
306
|
+
|
|
307
|
+
```sh
|
|
308
|
+
npm install -g @satanwagen/reviewkit-cli
|
|
309
|
+
cd my-site
|
|
310
|
+
reviewkit # first run pairs the repo (one click in the review panel), then the live view
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Pairing is one click, no copy-paste.** Bare `reviewkit` in an unpaired repo runs `setup` first; it detects the site's `token` (and a custom `syncToken` / `syncUrl`, if any) in the repo, opens a loopback port and waits. In the browser open the review panel → gear → **Connect terminal / agent…** → **Connect**: the panel finds the waiting terminal and pushes the connection into `<repo>/.reviewkit/project.json` (gitignored). `setup` also registers the MCP server in Claude Code and Cursor when it finds them. The dialog keeps a copyable key as the fallback for a terminal on another machine.
|
|
314
|
+
|
|
315
|
+
The live view is interactive: arrow keys walk the items, Enter opens actions (detail, apply this item with Claude Code, copy as prompt, mark applied / accepted / rejected / reopen, delete), `a` hands every open item to Claude Code in the repo, and the header shows who is reviewing right now in their own colours. Everything is the shared room state, so a status flipped in the terminal or by an agent changes in reviewers' browsers instantly, and vice versa.
|
|
316
|
+
|
|
317
|
+
Everyday commands: `reviewkit list --status open`, `reviewkit show <id>`, `reviewkit status <id> applied`, `reviewkit watch`, `reviewkit export -o review.json`, `reviewkit prompt` (agent-ready work list), `reviewkit rooms`, `reviewkit projects` / `-p <name>` to act on another paired site. Full table in [`cli/README.md`](./cli/README.md).
|
|
318
|
+
|
|
319
|
+
**Agents.** With the bridge registered, "apply the reviewkit feedback" is enough: the agent calls `reviewkit_prompt`, edits code, then `reviewkit_set_status(id, "applied")` per item. Manual registration:
|
|
320
|
+
|
|
321
|
+
```sh
|
|
322
|
+
claude mcp add --scope user reviewkit -- npx -y @satanwagen/reviewkit-cli mcp
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Requirements: the site has live sync on (the default), Node 21+. The CLI reads shared rooms, never a reviewer's local browser storage. The sync server gained a `rooms` request (protocol v1, additive) so the CLI can find every page with review data; run the 0.1.4 server build.
|
|
326
|
+
|
|
240
327
|
## Pairing with Emblema
|
|
241
328
|
|
|
242
329
|
ReviewKit pairs with the [Emblema](https://emblema.app) desktop app: once paired, the local sync agent streams review items to Emblema, and the paired Emblema shows them as findings in its ReviewKit tile (statuses flow back to the page).
|
|
243
330
|
|
|
244
|
-
Sync is automatic and realtime — no manual step. With `emblema`
|
|
331
|
+
Sync is automatic and realtime — no manual step. With `emblema={{}}` in the `ReviewKit` config, the browser itself pushes every added or edited open item to a locally running Emblema (debounced 500 ms, silent-fail: an unreachable Emblema never blocks the reviewer; the panel header shows a small `emblema` status dot). The session menu keeps **Resend all to Emblema** as a one-click fallback that re-dispatches every open item on the page.
|
|
332
|
+
|
|
333
|
+
**Which repo gets edited** is Emblema's knowledge, not the website's: the intake advertises its `repoPath` in the `/intake/ping` answer, and the local agent has it in `.reviewkit/emblema-sync.json` (`init --repo`, default: the directory you ran it in). `emblema.repoPath` remains as an optional override only; an absolute path from a reviewer's laptop has no place in host code.
|
|
245
334
|
|
|
246
335
|
**Where the key lives:** the pairing key and state are stored by the local sync agent in `<project>/.reviewkit/emblema-sync.json`. The key (`emk_…`) is a secret — the panel only ever displays it shortened (`emk_…last4`) and puts the full value on the clipboard.
|
|
247
336
|
|
|
248
|
-
**Set up and generate a key** (in the project repo):
|
|
337
|
+
**Set up and generate a key** (in the project repo; the CLI is its own package so host installs stay CLI-free):
|
|
249
338
|
|
|
250
339
|
```sh
|
|
251
|
-
npx reviewkit-emblema init --origin https://your-site
|
|
252
|
-
npx reviewkit-emblema serve
|
|
340
|
+
npx -p @satanwagen/reviewkit-cli reviewkit-emblema init --origin https://your-site --sync-url wss://your-site/rk-sync --sync-token <secret>
|
|
341
|
+
npx -p @satanwagen/reviewkit-cli reviewkit-emblema serve # local API + realtime bridge (bridge needs both sync flags)
|
|
253
342
|
```
|
|
254
343
|
|
|
255
344
|
Paste the printed key into Emblema. From the page UI the same lives in the panel's gear menu → **Connect to Emblema…** — the menu entry shows the live pairing status at a glance (green dot = paired, amber = the local agent isn't answering, red = key revoked, grey = not paired), and the dialog has **New key (rotate)**, **Copy key** and **Delete key (revoke)**.
|
|
@@ -257,22 +346,24 @@ Paste the printed key into Emblema. From the page UI the same lives in the panel
|
|
|
257
346
|
**Rotate / revoke from the CLI:**
|
|
258
347
|
|
|
259
348
|
```sh
|
|
260
|
-
npx reviewkit-emblema key # print the current key
|
|
261
|
-
npx reviewkit-emblema rotate # issue a new secret — the old key stops working immediately
|
|
262
|
-
npx reviewkit-emblema revoke # disable the pairing entirely (rotate re-enables)
|
|
349
|
+
npx -p @satanwagen/reviewkit-cli reviewkit-emblema key # print the current key
|
|
350
|
+
npx -p @satanwagen/reviewkit-cli reviewkit-emblema rotate # issue a new secret — the old key stops working immediately
|
|
351
|
+
npx -p @satanwagen/reviewkit-cli reviewkit-emblema revoke # disable the pairing entirely (rotate re-enables)
|
|
263
352
|
```
|
|
264
353
|
|
|
265
354
|
Rotate and revoke take effect immediately, including against an already-running `serve` process — authenticated requests always check the current on-disk key, so a rotated or revoked key can never keep authenticating.
|
|
266
355
|
|
|
267
|
-
|
|
356
|
+
If the host sends a CSP, the bridge needs `connect-src http://127.0.0.1:*` (see "CSP").
|
|
357
|
+
|
|
358
|
+
## Demos
|
|
268
359
|
|
|
269
360
|
```sh
|
|
270
361
|
cd examples/vite-demo
|
|
271
362
|
npm install
|
|
272
|
-
npm run dev # http://localhost:3010 —
|
|
363
|
+
npm run dev # http://localhost:3010 — devAutoOn keeps the layer on in dev
|
|
273
364
|
```
|
|
274
365
|
|
|
275
|
-
Also try `http://localhost:3010/?review=demo` to exercise URL activation.
|
|
366
|
+
Also try `http://localhost:3010/?review=demo-review` to exercise URL activation. `examples/next-demo` is the same site on Next.js (App Router, `/next` entry, CSP headers) on port 3011.
|
|
276
367
|
|
|
277
368
|
## Development
|
|
278
369
|
|
|
@@ -281,4 +372,7 @@ npm install
|
|
|
281
372
|
npm run typecheck
|
|
282
373
|
npm run lint
|
|
283
374
|
npm run build # tsup → dist/ (ESM + CJS + d.ts)
|
|
375
|
+
npm test # CLI tests + activation matrix (needs dist/)
|
|
284
376
|
```
|
|
377
|
+
|
|
378
|
+
See `CHANGELOG.md` for the 0.1.3 default changes and migration notes.
|