@cychien/ezdesign 0.9.2 → 0.11.0

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.
Files changed (34) hide show
  1. package/README.md +209 -550
  2. package/dist/cli.mjs +2640 -655
  3. package/dist/constants.d.ts +8 -4
  4. package/dist/extension/g/0.11.0-75c3fbebc3/dialog.js +1 -0
  5. package/dist/extension/g/0.11.0-75c3fbebc3/media.js +1 -0
  6. package/dist/extension/g/0.11.0-75c3fbebc3/page.js +44 -0
  7. package/dist/extension/g/0.11.0-75c3fbebc3/worker.js +57 -0
  8. package/dist/extension/manifest.json +15 -12
  9. package/dist/home.css +4706 -0
  10. package/dist/home.js +440 -0
  11. package/dist/native-host.mjs +33804 -115
  12. package/dist/overlay.css +5 -1
  13. package/dist/overlay.js +73 -22
  14. package/dist/shell.css +297 -1
  15. package/dist/shell.js +1230 -358
  16. package/dist/video-digest.js +16388 -0
  17. package/dist/vite.mjs +5 -1
  18. package/package.json +4 -3
  19. package/skills/ezdesign/SKILL.md +7 -3
  20. package/skills/ezdesign/reference/start.md +2 -1
  21. package/dist/extension/g/0.9.2-d9aee83dae/dialog.js +0 -1
  22. package/dist/extension/g/0.9.2-d9aee83dae/media.js +0 -1
  23. package/dist/extension/g/0.9.2-d9aee83dae/page.js +0 -39
  24. package/dist/extension/g/0.9.2-d9aee83dae/worker.js +0 -57
  25. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/dialog.css +0 -0
  26. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/dialog.html +0 -0
  27. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/icon-128.png +0 -0
  28. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/icon-16.png +0 -0
  29. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/icon-32.png +0 -0
  30. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/icon-48.png +0 -0
  31. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/media.html +0 -0
  32. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/setup.css +0 -0
  33. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/setup.html +0 -0
  34. /package/dist/extension/g/{0.9.2-d9aee83dae → 0.11.0-75c3fbebc3}/setup.js +0 -0
package/README.md CHANGED
@@ -5,53 +5,85 @@
5
5
  </picture>
6
6
  </h1>
7
7
 
8
- > Point at your live app. Your agent fixes it.
8
+ **The best way to design with AI agents.** Click anything in your running app, say what should
9
+ change, and your coding agent edits the exact line of code.
9
10
 
10
- ezdesign turns any locally running dev server into an annotatable review surface for human ↔
11
- agent iteration. The user marks up the **real page** - click an element, frame a region, select
12
- some text, leave a comment - and the feedback flows to a local coding agent (Claude Code, Codex,
13
- anything that can run a CLI) as structured items that resolve to **exact source locations**. The
14
- agent edits the code, HMR updates the page in place, the user reviews the next round. Beside the
15
- app, **canvases** hold HTML designs the agent draws, for exploring ideas before (or instead of)
16
- changing the real thing.
11
+ [![npm](https://img.shields.io/npm/v/@cychien/ezdesign?color=e56d41&label=npm)](https://www.npmjs.com/package/@cychien/ezdesign)
12
+ [![license](https://img.shields.io/badge/license-MIT-171717)](#license)
17
13
 
14
+ ```sh
15
+ npx -y @cychien/ezdesign@latest http://localhost:5173 --agent claude
18
16
  ```
19
- $ npx -y @cychien/ezdesign@latest http://localhost:5173/pricing --agent claude # managed ACP agent
20
- $ npx -y @cychien/ezdesign@latest http://localhost:5173/pricing # portable poll mode
21
- $ npx -y @cychien/ezdesign@latest poll # agent waits for feedback
22
- $ npx -y @cychien/ezdesign@latest --agent claude # no dev server: canvases only
17
+
18
+ Works with **Claude Code**, **Codex** and **Gemini CLI**. Open source, runs on your machine, no API
19
+ key. See it in action at **[ezdesign.dev](https://ezdesign.dev)**.
20
+
21
+ ## Why
22
+
23
+ Describing UI changes in chat is lossy. "Make the second card's title a bit bigger" leaves your
24
+ agent guessing which card, which file and how much. Designing in Figma first means building
25
+ everything twice.
26
+
27
+ ezdesign puts the review on the real page. You point, it tells the agent where that thing lives in
28
+ your source, and the change appears in place a moment later through your dev server's hot reload.
29
+
30
+ ```
31
+ you ezdesign your agent
32
+ ─── ──────── ──────────
33
+ click the hero title ──▶ src/sections/Hero.tsx:13 ──▶ edits that line
34
+ "make this 56px" <Hero ← App> page hot-reloads
23
35
  ```
24
36
 
25
- - **Zero config.** A local daemon reverse-proxies your dev server and injects the annotation
26
- overlay. No code changes, no build plugins required, works with any framework.
27
- - **Anchored feedback.** Annotations carry a layered anchor: `file:line` (with the optional Vite
28
- plugin), React component chain, `data-section`, CSS selector, text, viewport. Agents stop
29
- guessing which part of the page you meant. Paste or drop a screenshot into any comment and the
30
- agent gets a path to it; `/element` points a comment at a second element, so "make this match
31
- that one" arrives as a `file:line` too.
32
- - **Every screen at once.** Review on one device or lay all three out on a grid - desktop,
33
- iPad and iPhone side by side at their real viewports, scrolling together. An annotation stays on
34
- the screen it was made on, and reaches the agent tagged with the width it was made at.
35
- - **Canvases beside the app.** Tabs next to the App hold boards of frames - complete HTML pages
36
- at real viewport widths that the agent adds, you arrange, and both of you annotate. `/element`
37
- crosses tabs, so "build [ref 1] into the app" points at a frame from an App comment.
38
- - **Built for the loop.** In ACP mode, ezdesign owns the agent and keeps its live work, questions,
39
- and permission prompts in the review shell. Poll mode keeps the blocking structured-JSON contract
40
- for any external agent or automation. HMR keeps iterations in place; the review chrome lives
41
- outside the app frame, so it survives your agent's syntax errors.
42
- - **Local-first.** Everything binds to 127.0.0.1. Nothing leaves your machine, except one
43
- request to the npm registry every few hours to learn whether a newer version exists (opt out
44
- with `EZDESIGN_NO_UPDATE_CHECK=1`).
45
-
46
- ## Install
47
-
48
- Nothing to install - `npx -y @cychien/ezdesign@latest` is enough. For agents, add the bundled skill:
37
+ ## Quick start
38
+
39
+ You need Node.js 22.13+ and an app running locally.
49
40
 
41
+ **1. Open a review** from your project's root, pointing at the page you want to work on:
42
+
43
+ ```sh
44
+ npx -y @cychien/ezdesign@latest http://localhost:5173/pricing --agent claude
50
45
  ```
46
+
47
+ A review shell opens in your browser with your app inside it and the agent beside it.
48
+
49
+ **2. Point and comment.** Press `E` and click an element, or `R` and drag over an area. Write what
50
+ should change. Repeat for as many things as you like.
51
+
52
+ **3. Send** with `⌘↵`. The agent works through the batch, streams what it is doing, and the page
53
+ updates in place. Review, comment again, send again.
54
+
55
+ ### Or start it from your agent
56
+
57
+ Add the skill once:
58
+
59
+ ```sh
51
60
  npx skills add cychien/ezdesign --skill ezdesign
52
61
  ```
53
62
 
54
- Optional, for exact `file:line` anchors in Vite + React projects:
63
+ Then type `/ezdesign` in Claude Code, Codex or Gemini CLI, and the agent opens the review for you.
64
+
65
+ ### Or use the Mac app
66
+
67
+ Download it from [Releases](https://github.com/cychien/ezdesign/releases/latest): `ezdesign-arm64.dmg`
68
+ for Apple silicon, `ezdesign-x64.dmg` for Intel. Pick a project in its sidebar and start its dev
69
+ server from the App tab; the agent connects on its own.
70
+
71
+ The app is not signed with an Apple Developer ID, so macOS stops it the first time:
72
+
73
+ 1. Open the DMG and drag **ezdesign** into **Applications**, then open it. macOS says it cannot
74
+ verify the developer; choose **Done**.
75
+ 2. Open **System Settings → Privacy & Security**, scroll to the note about ezdesign, choose
76
+ **Open Anyway**, and confirm.
77
+
78
+ After that it opens normally. (In Terminal, `xattr -dr com.apple.quarantine /Applications/ezdesign.app`
79
+ does the same.) New versions install from inside the app: when one is out, the foot of the sidebar
80
+ offers **重新啟動以更新**, and the app restarts on it. A copy opened straight from the DMG cannot
81
+ replace itself, so drag it into Applications first.
82
+
83
+ ### Exact `file:line` for Vite + React
84
+
85
+ ezdesign works with any framework, no code changes. For Vite + React, add the plugin and every
86
+ annotation carries the exact source line:
55
87
 
56
88
  ```ts
57
89
  // vite.config.ts
@@ -62,554 +94,181 @@ export default defineConfig({
62
94
  })
63
95
  ```
64
96
 
65
- Dev-only (`apply: 'serve'`) - it never touches production builds.
66
-
67
- ## Updating
68
-
69
- When a newer ezdesign is on npm, a card appears at the top of the review shell and one button takes
70
- it. The new version is installed under `~/.ezdesign/versions/` and started as this daemon's
71
- successor: the successor binds and registers first, and only once it is up does the old daemon let
72
- go - so a version that fails to start leaves you on the one you had, with the error in the card.
73
- Your sessions come back on the ports they held and the shell reloads itself.
74
-
75
- An ACP agent is restarted along with the daemon and asked to resume the conversation it was having,
76
- so an update no longer costs the review its context. An agent that cannot resume comes back in a
77
- fresh session, the same as `/new`, and the thread says so when it happens.
78
-
79
- The close button in the card's corner puts the offer away; the version beside the name in the
80
- header turns into a pill you can click to bring it back. Nothing is installed or restarted without
81
- that click.
82
-
83
- Without the shell:
84
-
85
- - **CLI** - always invoke it as `npx -y @cychien/ezdesign@latest`. A bare `npx @cychien/ezdesign`
86
- reuses whatever version npx cached and never checks for a newer one.
87
- - **Daemon** - opening a session with a different CLI version replaces the running daemon with that
88
- version, under the same restore guarantee any daemon restart gets (see Configuration). A CLI
89
- that reaches a daemon on another version is refused with a `409` that says how to update,
90
- instead of speaking a mismatched protocol.
91
- - **Skill** - `npx skills update ezdesign`, whenever you feel like it. Deliberately not part of the
92
- update button: the skill's own instructions launch the CLI as `npx -y @cychien/ezdesign@latest`,
93
- so an old copy still starts a current review, and everything that governs a review once it is
94
- running ships with the daemon. Its reader is also an agent session ezdesign did not spawn and
95
- cannot restart, so a sync would not take effect until that agent's next session either way. The
96
- bundled copy carries the release it came from in its frontmatter (`metadata.version`).
97
-
98
- The daemon asks the npm registry for the latest version at most once every four hours. Set
99
- `EZDESIGN_NO_UPDATE_CHECK=1` to never ask.
100
-
101
- ## Annotating
102
-
103
- Two modes, and the toolbar shows which one is armed. Both leave the page live underneath, and
104
- `Esc` steps back out - first the open comment box, then the mode.
105
-
106
- - **元素** (`E`) - point at one thing. Hover frames the element under the cursor, a click opens the
107
- comment box on it, and selecting text instead annotates that run. This is the mode for "this
108
- button", "this heading", "this sentence".
109
- - **範圍** (`R`) - drag a box over an area. What the box encloses *whole* is what it means: the
110
- elements it merely cuts through are looked past, down to the ones that fit - so framing a card is
111
- the card, and framing a row of them is the row. The box you drew stays on screen as the comment's
112
- subject, and the agent is handed the enclosing element's `file:line` plus `anchor.contains`, a
113
- line per element the box held: component, `file:line`, and a snippet of that element's own text,
114
- which is what tells two instances of the same component apart. This is the mode for "this whole
115
- block", "these four cards", "the spacing through here".
116
-
117
- One comment is composed at a time - across every preview on the grid, not just the one you are
118
- in - so a box drawn in one screen cannot open a second comment box behind the one you are writing.
119
-
120
- ## The comment box
121
-
122
- Typing `/` in either comment box opens a command menu - arrow keys and Enter, or click. A slash
123
- mid-word stays a slash, so urls and paths are left alone. The commands:
124
-
125
- - **`/file`** opens the system file picker and drops what you choose in as an attachment, where the
126
- slash was.
127
- - **`/new`** starts a new chat: the agent's context is thrown away and the review carries on in a
128
- fresh session, which is how a long review stops paying to replay its own history on every turn.
129
- Only offered in [ACP mode](#acp-mode-experimental), and only in the note box below the queue. The
130
- thread empties with it, back to the state it opened in - a notice explaining that the history above
131
- no longer counts is still history above. The log on disk is untouched: `/new` records where the
132
- visible thread starts, so the record of the review stays whole while the shell shows the fresh
133
- start that was actually asked for. Every batch the agent had not finished with goes too, not just
134
- the turn it was on - a question queued behind that turn was asked of a context you have just said
135
- to start over from, and a fresh session answering it would be answering something else. The 待送
136
- 清單 is left alone: those you have not sent yet, so they were never the old context's to begin
137
- with. A note you were part-way through typing survives, so `/new` and then send is one move.
138
- One thing does carry over: the first batch of the new chat is handed what you asked for in the
139
- earlier ones, one line each, marked as already handled. The agent works to `PRODUCT.md`, starts
140
- from `DESIGN.md`, and is told to write a correction into them when it has come up before; a fresh
141
- session cannot see "before" on its own, so the daemon shows it.
142
- - **`$`** opens a second menu, of the skills this machine can run, and picking one
143
- asks the agent to run it over the batch you are about to send. The skill shows as a pill above
144
- the box until the batch goes, and on the sent bubble afterwards - which is the only place it
145
- appears, because an explicitly invoked skill is expanded into the agent's prompt rather than
146
- loaded through its skill tool, so nothing in the turn's activity would otherwise say it ran.
147
- A skill on its own is a batch: "run this over what you can see" needs no annotation and no note.
148
-
149
- Its own symbol rather than more rows under `/`, because the two are different kinds of thing:
150
- `/file` and `/element` act on the box you are typing in, and a skill is handed to the agent.
151
- Only offered in [ACP mode](#acp-mode-experimental), and only in the note box.
152
-
153
- The list is read off disk - `.claude/skills` and `.agents/skills`, in the project and in your
154
- home directory, plus `.claude/plugins/*/skills`. Skills bundled inside the Claude Code binary
155
- are **not** listed: they have no file to find, and the protocol's own command list cannot tell a
156
- skill from `/doctor`, so including it would mean a menu of "skills" that offers things that are
157
- not skills.
158
- - **`/element`** points the comment at a *second* element - "make this match that one". The page
159
- stays live while you choose, so a plain click still follows links and opens menus and only
160
- ⌘/Ctrl+click picks; the comment box steps aside and comes back when you are done. You can cross to
161
- another page to find what you meant, and ezdesign brings you back to finish the comment. The
162
- element arrives as a chip carrying the same layered anchor an annotation gets, so the agent is
163
- handed a `file:line` instead of a description, and `[ref 1]` in the comment text says exactly where
164
- in the sentence you meant it.
165
-
166
- The same command also takes a plain **drag**, the way a screenshot tool does: frame a box and the
167
- chip points at everything inside it (no modifier needed - a press only becomes a box once it has
168
- moved, so a plain click is still the page's). What the box encloses whole is what it means - the
169
- elements it merely cuts through are looked past, down to the ones that fit - so framing a card is
170
- the card, and framing a row of them is the row. A region resolves to their common ancestor for the
171
- `file:line`, and carries `anchor.contains`, a line per element it enclosed - component, `file:line`
172
- and a snippet of that element's own text, which is what tells two instances of the same component
173
- apart. A frame around exactly one element is simply that element, the same as clicking it.
174
-
175
-
176
- Either comment box also takes an image or file, pasted or dropped in. Attachments become inline chips
177
- in the text itself, so a comment can point at a file mid sentence, and backspace deletes one the
178
- way it deletes a character. They are names, not previews - the box is for the comment, and a
179
- thumbnail takes the room it needs. The bytes go straight to the session directory, capped at 8 MB
180
- per file, and the batch hands the agent an absolute path per attachment so it opens the screenshot
181
- instead of being handed base64. Deleting a queued annotation deletes its files with it; anything
182
- attached and then abandoned is collected a day later.
183
-
184
- ## The queue
185
-
186
- Queued annotations stay editable until the batch goes out. The pencil on a row turns that row's
187
- comment into a field in place - nothing appears elsewhere, and there is no second place to look.
188
- Editing borrows the two buttons the row already has rather than adding any: the pencil becomes the
189
- tick that commits, and the cross, which dismisses either way, becomes the one that backs out. The row
190
- keeps its number and its source label throughout, so what is being changed never stops being obvious.
191
-
192
- The field is seeded with exactly what the row was showing, chips where the sentence had them, so a
193
- `[ref 1]` or an attached screenshot survives a wording change instead of having to be picked again.
194
- ⌘/Ctrl+Enter saves, Escape cancels, and both outrank the shell's own bindings while the field is
195
- open, so ⌘+Enter is this save rather than the batch send. `/file` works inside it; `/element` does
196
- not - pointing a comment at a new element needs the page's own pointer routed back into a row, which
197
- is a different thing from adjusting what you already wrote, so delete the chip and re-annotate
198
- instead.
199
-
200
- A cancel takes any file attached during the edit with it; the files the row already had are left
201
- alone either way, and one dropped by a save is collected by the same sweep that collects an
202
- attachment nothing references.
203
-
204
- ## Asking while the agent is still working
205
-
206
- Sending mid-turn is allowed and queues - you should not have to wait for a turn to finish before
207
- writing down what you just noticed. The batch goes out on its own the moment the agent comes back,
208
- and your bubble appears in the thread the moment it leaves the composer, which is what says it went.
209
- To make it go *now*, stop the current turn: the queued batch is delivered as soon as that turn ends.
210
- `/new` does the opposite - it drops the queue along with the context.
211
-
212
- The thread draws each answer under the question it answers, which is not always where the log put it.
213
- The log itself stays append-only and stamped in real time - it is the record of the review - but a
214
- batch sent mid-turn lands *between* an earlier question and its reply, so rendering the log verbatim
215
- would put the agent's answer under a question it is not answering, and put the new question above
216
- output that started before it was typed. Instead every entry carries the batch it belongs to, and the
217
- in-flight turn is drawn in the same place its finished reply will be, so nothing moves when the reply
218
- lands. Entries that answer no batch - the session notices - stay exactly where the log put them,
219
- because for those the timeline *is* the meaning.
220
-
221
- ## Devices
222
-
223
- The header carries one toggle group with two sides: a menu picking the size to preview at, and
224
- all of them at once (`4`). The raised side is the one showing. The sizes are real device viewports, not bare widths - where the fold lands is
225
- half of what a responsive review is looking at:
226
-
227
- | Key | Device | Viewport |
228
- | --- | --- | --- |
229
- | `1` | 手機 | 375×629 - a 375×812 screen, less the browser's bars |
230
- | `2` | 平板 | 1112×740 - a 10.5" tablet on its side, past the breakpoint most layouts switch on |
231
- | `3` | 電腦 | 1440×788 - a 1440×900 laptop screen, less the menu bar and the browser's own |
232
-
233
- The sizes are the **page area** a browser leaves on that device, not the screen it leaves it on -
234
- so a card on the grid stops showing the page exactly where the real thing stops showing it. On
235
- its own a size takes only its width from the table and the height from your screen: one preview
236
- gets the whole stage to show the page in, and the grid is where the fold is what you are
237
- checking.
238
-
239
- The names are shelves, not machines - a 手機 row naming a handset would claim a precision the
240
- row does not have, when phone widths run from 360 up past 430. The sizes are still taken from
241
- real machines; the comments in `devices.ts` say which.
242
-
243
- **`4` lays them out on one grid** - phone and tablet side by side, the desktop on the row below.
244
- The control in the stage's top corner picks which sizes are on it: the three above, plus a portrait
245
- tablet (834×1018) that is off by default.
246
-
247
- The grid is drawn at **true size**, always. A 375 card is 375 pixels whether it is standing alone
248
- or with three others beside it - a scaled 375 is not 375 to a media query, and a page measured at a
249
- size nobody browses at is not a measurement. Turning a size off does not resize or rearrange the
250
- ones that are left. What does not fit is what the dragging is for.
251
-
252
- Every card is the device it names, at its own height. What keeps the grid short enough to work
253
- on is the two-row layout, not a cap on the cards - a card cut to a shared height would be lying
254
- about where the fold falls, which is half of what the preview is for.
255
-
256
- **Drag a card by its label to rearrange the grid.** A card cannot stop just anywhere: it lands
257
- top-aligned in an existing row, or on a row of its own, and a line marks where it would go -
258
- upright between two cards, flat between two rows. The arrangement is remembered, and a size turned
259
- on later joins as its own row at the bottom rather than reshuffling what you laid out.
260
-
261
- **Drag the background to move the grid.** The frames are the app: a press inside one belongs to
262
- the page, so the backdrop and the gaps are what the grid is panned by. That is also why the
263
- grid does not have to fit - what is off the edge is a drag away.
264
-
265
- Scrolling inside any one preview scrolls the others to the same point in the page, and navigating
266
- one - a link, back, forward - takes all of them: annotations are filtered by path, and three
267
- previews on three pages is three separate reviews.
268
-
269
- An annotation belongs to the screen it was made on: mark up the iPhone card and the pin lives
270
- there, not on all three. The agent gets `@mobile 390x844` in the anchor either way. Everything the
271
- overlay draws - the comment box, the element label, the pins - is drawn back up to full size, so a
272
- grid scaled to 60% is still one you can read and type into.
97
+ It only runs in dev (`apply: 'serve'`) and never touches production builds.
273
98
 
274
- ## Canvases
99
+ ## Features
275
100
 
276
- A canvas is an infinite board of frames. Each frame is one complete, self-contained HTML document -
277
- its own `<head>`, its own styles, scripts and CDN assets allowed - rendered in its own iframe at a
278
- real viewport width, so a 390 frame is a phone to its media queries however far the board is zoomed
279
- out. No CSS is shared between frames: exploration directions diverge, and that is the point.
280
-
281
- **Tabs.** The strip above the stage has an **App** tab while a dev server is attached, then one tab
282
- per open canvas. `+` makes a new one, a double-click on a tab renames it, and closing a tab
283
- archives the canvas: the tab goes, the files stay. **Canvas** lists every canvas, open and
284
- archived, and reopens one; hover a row for **刪除**, which removes the canvas and all its frames
285
- (after asking). A session has one active tab:
286
- two shell windows follow each other, and every batch tells the agent which tab you sent it from.
287
-
288
- Run `ezdesign` with no url in a project without a dev server and the shell opens on that
289
- project's canvases - one empty `未命名 canvas` the first time. Run `ezdesign <url>` later and the
290
- App tab joins the same shell; the conversation carries on.
291
-
292
- **The board.** Drag the background or hold Space to pan, scroll to pan, pinch or Cmd/Ctrl+scroll to
293
- zoom around the pointer, `-`/`+` in the corner to step, `⇧1` to fit everything, `⇧0` to go back to
294
- 100%. Click a frame to select it, Shift+click to add, `⌘A` for all; drag to move, with guides that
295
- snap to other frames' edges and centres; `⌘D` duplicates, `⌫` deletes, arrows nudge (`⇧` for 10). A
296
- frame keeps the size it was added at: it is a viewport, so it is never resized. Every step undoes
297
- and redoes (`⌘Z`, `⌘⇧Z`). Double-click a frame to scroll it or click through its controls; Escape
298
- hands the pointer back. Positions, the open tabs, the active tab and each canvas's camera survive a
299
- reload and a daemon restart.
300
-
301
- **Annotating.** `元素` and `範圍` work inside frames as they do on the app, and an annotation in a
302
- frame reaches the agent as a line of that frame's file -
303
- `.ezdesign/canvases/<canvas>/frames/<frame>.html:14 · [canvas frame "Hero A" on canvas "首頁探索"]`.
304
- `/element` crosses tabs both ways: start it in an App comment, switch to a canvas, Cmd+click the
305
- frame you mean, and the comment comes back with the ref in it - or the reverse.
306
-
307
- **The agent.** An ACP agent gets the `ezdesign-canvas` tools - `list_canvases`, `create_canvas`,
308
- `read_canvas`, `reopen_canvas`, `add_frame`, `update_frame`, `remove_frame` - from its first turn,
309
- granted without asking. New frames go on the canvas you sent from, or else the newest open one; the
310
- agent starts a new canvas only when none is open or you ask for one. A canvas it creates becomes the
311
- active tab and its frames appear as each call returns. A frame's HTML is the agent's: it changes it
312
- through the tools or by editing the file, and the open shell reloads that frame either way. The agent
313
- picks a frame's size when it adds it; after that, where the frame sits is yours, and the tools
314
- refuse geometry on a frame that exists. Poll mode has no canvas tools.
315
-
316
- **On disk.** Canvases live in the project, in `.ezdesign/canvases/<canvas>/`: `canvas.json` (name,
317
- frames), `layout.json` (where each frame is) and `frames/<frame>.html`. ezdesign writes the two
318
- JSON files and never reads back an edit made to them by hand; frame files are yours to edit. It
319
- does not touch your `.gitignore`: committing canvases is your call.
320
-
321
- **Isolation.** Frame documents are agent-written pages running third-party scripts, so they are
322
- served from `http://localhost:<port>` while the shell stays on `http://127.0.0.1:<port>` - another
323
- site. A frame cannot read the shell's storage, script it, or call the session's api, and the
324
- overlay inside it reaches the review through the shell. Two shortcuts do not fire while focus is
325
- inside a frame: send (`⌘↵`) and stop (`⌘.`).
101
+ - **Point, don't describe.** Click an element, drag over a region, or select a run of text. Each
102
+ annotation reaches the agent with its source `file:line`, React component chain, selector, text
103
+ and viewport.
104
+ - **Every screen size at once.** Preview at phone, tablet or desktop, or lay them all out on one
105
+ grid at true size, scrolling and navigating together.
106
+ - **Explore before you commit.** Ask for a few versions of any element and flip through them on the
107
+ real page. Keep the one you like.
108
+ - **Canvases beside your app.** The agent draws whole design directions as real HTML pages at real
109
+ widths, on a board next to your app. Pick one and it builds that into your components.
110
+ - **A design library from the whole web.** The Chrome extension saves any image, video or region
111
+ of a page, with tags and a note. Pull those into a comment as references.
112
+ - **The agent lives in the shell.** Its replies, plan, tool calls, questions and permission
113
+ prompts all show up next to your app. No terminal to babysit.
114
+ - **Local-first.** Your code never leaves your machine, and ezdesign uses the agent you already
115
+ have, so there is nothing new to pay for.
326
116
 
327
- ## Configuration
117
+ ## Using the review shell
118
+
119
+ ### Annotating
328
120
 
329
- | Variable | Default | What it moves |
121
+ | Key | Mode | Use it for |
330
122
  | --- | --- | --- |
331
- | `EZDESIGN_DATA_DIR` | `~/.ezdesign` | Session state, the daemon registry and log, versions installed by the in-shell update, and the update-check cache |
332
- | `EZDESIGN_CONTROL_PORT` | `4400` | First port of the ten-port range the daemon searches for its control server |
333
- | `EZDESIGN_NO_UPDATE_CHECK` | unset | Set to `1` and the daemon never asks the npm registry for the latest version, so the shell never offers an update |
334
- | `EZDESIGN_NO_IDLE_STOP` | unset | Set to `1` and the daemon never stops itself for being idle; something else, like `npm run dev`, owns its lifetime |
335
- | `EZDESIGN_DESIGN_HARNESS` | unset (off) | Set to `1` in the environment the daemon starts from and the experimental 增強設計 design harness is available; unset, the shell has no switch and no agent is asked to use it |
336
- | `EZDESIGN_INSPIRE_URL` | `https://inspire.ezdesign.dev` | The inspiration database the `inspire` commands query |
337
- | `EZDESIGN_API_URL` | `https://api.ezdesign.dev` | The ezdesign backend the review shell signs in through |
338
-
339
- Set `EZDESIGN_DATA_DIR` to run a second, fully isolated instance: a starting daemon only adopts a
340
- live daemon that serves its own data dir, so instances sharing a control range each take their own
341
- port in it. `npm run dev` sets both.
342
-
343
- Sessions outlive the daemon that served them. On start, the daemon picks each session back up
344
- from disk and re-binds it to the port it last held, so a review shell tab you already have open
345
- reconnects by itself, and feedback you queued before the restart is still waiting. If that port
346
- has since been taken, the session moves to a free one and the CLI re-resolves it. A daemon brings
347
- back the sessions that were live when it went down; one that stopped itself after half an hour
348
- with no shell open brings back none, and the next `ezdesign` or `poll` in a project wakes that
349
- project's session. Only time awake counts toward that half hour, so a laptop that sleeps with a
350
- shell open wakes to a review that is still there. A shell left open while its daemon is gone says
351
- so, and the `ezdesign` that brings the session back finds it reconnecting instead of opening a
352
- second window. A session that was driving an ACP agent starts that agent again **and asks it to
353
- resume the conversation they were having**, so the review carries on where it stopped. Whether
354
- that works is the agent's answer to give - a transcript it no longer has, or an agent that cannot
355
- resume at all, leaves the review in a fresh context, and only then does the thread say so.
356
-
357
- A session belongs to a project, not to a dev-server origin. The project is the nearest `.git` or
358
- `package.json` ancestor of the directory you run `ezdesign` from. A dev server that comes back on
359
- another port keeps the review: `ezdesign http://localhost:5174/` in the same project points the
360
- open shell at the new port, with the conversation and the queue intact. Dev servers all default to
361
- the same port, so one origin is attached to one project at a time: opening a second project on
362
- `localhost:5173` moves the origin to it, and the first project keeps its conversation and queue and
363
- only loses its app until you attach one again. `poll`, `progress` and `end` find the session by the
364
- project they run from; a url passed to them is only checked against the attached app, with a note
365
- when it differs.
366
-
367
- Sessions from versions before this one were per origin. The first daemon of this version folds them
368
- into project sessions, keeping every queued annotation, batch and conversation, and moves the old
369
- directories to `sessions-legacy/` in the data dir, never deleting them. A daemon older than this
370
- version does not read the new format, so going back to one starts with no sessions.
371
-
372
- ## Agent modes
373
-
374
- ezdesign can either manage an ACP agent inside the review session or expose feedback through its
375
- portable polling contract. Both modes receive the same structured feedback and exact anchors.
376
-
377
- ### ACP mode (experimental)
378
-
379
- Pass `--agent` when opening the session:
123
+ | `E` | **Element** | One thing: a button, a heading, a sentence. Click it, or select text. |
124
+ | `R` | **Region** | An area: a card, a row of cards, "the spacing through here". Drag a box; whatever it fully encloses is what you meant. |
125
+ | `Esc` | | Close the comment box, then leave the mode. |
380
126
 
381
- ```
382
- npx -y @cychien/ezdesign@latest http://localhost:5173/ --agent claude
383
- ```
127
+ Annotations queue up beside your app and stay editable until you send. Paste or drop a screenshot,
128
+ or any file, straight into a comment and the agent gets a path to it.
129
+
130
+ ### Commands in the comment box
384
131
 
385
- The daemon starts the agent as a child process, sends each feedback batch as one turn, and shows
386
- the agent's streaming reply, plan, tool activity, questions, and permission prompts in the review
387
- shell. The completed reply is saved in the conversation, and the next queued batch is delivered
388
- automatically. No second terminal or `poll` loop is needed. This is also the mode used by the
389
- bundled ezdesign skill.
390
-
391
- Three controls come with owning the agent:
392
-
393
- - **⌘/Ctrl+.** stops the turn in flight, from anywhere including mid-sentence in the composer -
394
- which is when you usually want it, since watching the agent head the wrong way is what prompts it.
395
- No button: a control that is live for the few seconds a turn lasts costs the composer a permanent
396
- slot to say so. The status badge reads `正在中止` until the agent answers, so the keypress is not
397
- silent. Whatever it had already said stays in the thread, and the batch is not handed back: you
398
- stopped it on purpose.
399
- - **`/new`** in the note box clears the agent's context. See [The comment box](#the-comment-box).
400
- Every conversation a review has had stays reachable: once there is more than one, a control at the
401
- head of the thread lists them by time and by how much was said, and picking one shows its thread
402
- and puts the agent back on it. While an earlier conversation is showing the control says so in
403
- colour, because feedback sent then joins *that* conversation and nothing else on screen would
404
- mention it.
405
- - **The pill above the note box** names the model the next batch will be answered by, and opens
406
- everything else the agent lets the session be set to - reasoning effort, permission mode, and any
407
- toggle it offers. It is a permanent slot because unlike a cancel it is *state*: it says what you
408
- are about to send your feedback to.
409
-
410
- What it offers is the model and, where the agent has one, the reasoning effort. Not the permission
411
- mode and not the fast-mode toggle: those are the agent's own configuration, and a review is not
412
- where they should be decided. A mode the *agent* changes is still reported in the thread - some
413
- models do not support every mode, and selecting one moves it as a side effect that outlives the
414
- model that caused it, so the problem worth solving there was the silence rather than the missing
415
- control.
416
-
417
- A switch keeps the conversation. The model changes on the running session, so nothing is replayed
418
- and nothing is forgotten - which is what makes it worth reaching for mid-review: point at the easy
419
- half on a cheap model, move up for the layout problem that needs it. Sending mid-turn is allowed
420
- and applies from the next turn.
421
-
422
- What the menu holds is whatever the agent reports, not a list ezdesign keeps. That matters because
423
- the list changes with the choice: pick a model with no reasoning levels and no fast mode and both
424
- options leave the menu, and the agent can move a value on its own - a refusal falling back to
425
- another model - which arrives here and redraws. Some models do not support every permission mode,
426
- and picking one **downgrades the mode** as a side effect; showing the mode is what keeps that from
427
- happening silently.
428
-
429
- Your pick is remembered for the session and re-asserted on the next one, so `/new` and a daemon
430
- restart do not hand the review back to the agent's default. Every switch goes into the thread, so
431
- a review read back later says which model answered which batch.
432
-
433
- The agent itself can be changed from the pill left of the model, which is where it belongs: the two
434
- answer one question between them - what is about to read this feedback - and the model is a choice
435
- *within* the agent, so changing the agent replaces the list the model was chosen from. That always
436
- starts a new conversation, and the shell says so before it does it: a session id belongs to the agent that issued
437
- it - Claude keeps its conversations in one store and Codex in another, and neither can resolve the
438
- other's - so there is no way to hand a conversation over. Nothing ezdesign owns is lost. Every earlier
439
- conversation stays in the thread's own picker with the agent that had it, and switching back finds it
440
- again, resumable by the agent that remembers it.
441
-
442
- The agent can design the way a designer works, through a five-step harness shipped with the daemon:
443
- product, stack, design, components, make. It is off by default: set `EZDESIGN_DESIGN_HARNESS=1` in the
444
- environment the daemon starts from to enable it, and until then the shell has no switch, no agent is asked
445
- to use it, and the remembered choice for each project is kept untouched for when it is on. With
446
- the flag on it is opt-in, experimental - the switch says 實驗 - and a
447
- premium feature: the **增強設計** switch on its own line above the agent and the model, marked with a
448
- gold sparkle, turns it on, and the choice is remembered for the project - across sessions, a dev
449
- server that moved ports, `/new` and daemon restarts - except that it starts off when the project's
450
- `PRODUCT.md` or `DESIGN.md` is gone, so turning it on asks again. Off, every batch is handled
451
- exactly as it was before the harness existed. The harness applies only to a turn whose prompt says
452
- it is on; after it has been on in a conversation, every later turn with it off is told so, because
453
- the agent still remembers what it read while it was on. Two files at the project root are what it
454
- works to. **`PRODUCT.md`** says what the product is for - who uses it, what it makes possible, what
455
- must not change. **`DESIGN.md`** ([the format](https://github.com/google-labs-code/design.md)) says
456
- what it looks like, in broad strokes - a few anchor values in YAML frontmatter and the character of
457
- the language in prose, with the full scales left to the design system. When both exist, every batch
458
- tells the agent to read them before a visual change, keep to `PRODUCT.md`, and take `DESIGN.md` as
459
- a direction rather than a rule: a document held to the letter makes everything look alike, so the
460
- agent may go past it. An ordinary change edits code only. The two files change
461
- when you ask for it, when a correction has come twice, or when you state a rule - only on what you
462
- actually said, never on what the agent reads into a send-back; a change that only leaves a line stale is mentioned in the reply, not edited. Between them,
463
- `.ezdesign/config.json` records the stack and the browser tool the agent looks at the page with. The
464
- installed skill does not carry the harness: it opens reviews, and the harness is reached only
465
- through the daemon with the flag on.
466
-
467
- Turning the switch on in a project that lacks either file asks first, in the card above the
468
- composer: 啟用增強設計 - 增強設計會建立 PRODUCT.md 與 DESIGN.md,是否繼續? 取消 leaves the switch off,
469
- because the harness cannot run without them. 繼續 turns it on and starts a setup turn at once, which walks the steps
470
- that build the missing files before any batch is worked. Switching off again before the agent has
471
- started that turn cancels it, and the thread says so. `PRODUCT.md` is written as a designer meets a
472
- client: the agent reads the project's own copy and assets first, then asks, three questions a round,
473
- what they could not answer - who it is for, what it makes possible, what must be preserved - each
474
- led by what it inferred, so you correct rather than dictate. Nothing about feel or style is asked there; that belongs to `DESIGN.md`, which
475
- is read off the running page the way a design-token extractor reads one: computed styles, counted,
476
- so the colour on three hundred elements is the surface and the one on two is the accent, and only
477
- the character and its anchors are written down; the format's own linter checks the result.
478
-
479
- How the agent then works - where it looks for precedent, the principles it builds to, how it
480
- writes a repeated correction back into the two files - lives in the bundled skill's `reference/`
481
- files, which the daemon points it at turn by turn. A piece of design is not judged by the agent
482
- that made it, since a maker praises its own work: it starts a subagent with a fresh context, which
483
- looks at the agent's screenshots of the change and scores it against the reaction good work gets on
484
- X and Dribbble, "super clean!", in a product held to the standard of Shopify, Stripe, Linear and
485
- Vercel - useful, super clean, clear, fitting, well made - and answers ship, refine or pivot. One review; a second only after a pivot,
486
- comparing the two directions. The reply never mentions the review. What you say about quality in so many words, or correct twice, is
487
- written to `.ezdesign/taste.md`, which the agent builds to and the reviewer judges by,
488
- so both move toward your eye. The daemon carries
489
- only the facts a skill cannot know: whether the switch is on, which files exist, where the page
490
- is, what the turn is for.
491
-
492
- Three built-in profiles map short names to ACP server commands:
493
-
494
- | Profile | Command |
132
+ Type `/` in a comment for these:
133
+
134
+ | Command | What it does |
495
135
  | --- | --- |
496
- | `claude` | `npx -y @agentclientprotocol/claude-agent-acp` |
497
- | `codex` | `npx -y @agentclientprotocol/codex-acp` |
498
- | `gemini` | `gemini --experimental-acp` |
136
+ | `/element` | Point at a second element, so "make this match that one" arrives as two exact locations. `⌘`-click to pick, or drag a box. Works across pages and across canvas tabs. |
137
+ | `/explore` | Ask for variants of the element you are commenting on, then try each one on the page. |
138
+ | `/library` | Attach references from your design library. |
139
+ | `/file` | Attach a file from disk. |
140
+ | `/new` | Start a fresh conversation with the agent (note box only). Earlier ones stay one click away. |
499
141
 
500
- Any other value is used as an ACP command line, so a custom server can be started with
501
- `--agent 'my-acp-agent --flag'`. Only one live ACP agent can own a session. End the session or stop
502
- the daemon before switching it to a different agent.
142
+ Type `$` in the note box to run one of your installed agent skills over the batch.
503
143
 
504
- ### Poll mode
144
+ ### Shortcuts
505
145
 
506
- Without `--agent`, the review uses the existing CLI contract:
146
+ | Key | Action |
147
+ | --- | --- |
148
+ | `⌘↵` | Send the queue to the agent. Sending while it is still working queues the batch for its next turn. |
149
+ | `⌘.` | Stop the agent's current turn. |
150
+ | `1` `2` `3` | Preview at phone (375), tablet (1112) or desktop (1440). |
151
+ | `4` | Show every size on one grid. Drag a card's label to rearrange, drag the background to pan. |
507
152
 
508
- ```
509
- npx -y @cychien/ezdesign@latest http://localhost:5173/
510
- npx -y @cychien/ezdesign@latest poll
511
- ```
153
+ ### Choosing the agent and model
512
154
 
513
- `poll` blocks until the user sends feedback, prints the structured batch as JSON, and exits. Run it
514
- from the project, like the command that opened the session. This
515
- mode remains available for scripts and integrations that consume the CLI contract directly.
155
+ The pills above the note box pick the agent and the model, plus reasoning effort where the agent
156
+ offers it. Switching models keeps the conversation, so you can move to a stronger model for the
157
+ hard part of a review and back again. Switching agents starts a new conversation.
516
158
 
517
- ## Inspiration
159
+ ## Agents
518
160
 
519
- Before it builds a piece of design, the agent looks for precedent: a curated collection of
520
- interfaces, each entry carrying the reason it works and the case where it would be the wrong
521
- choice. It is searched by meaning - a description of what the piece has to show - and entries carry
522
- tags in their curator's own words, which nudge a search that names one. It needs a token:
161
+ | `--agent` | Agent |
162
+ | --- | --- |
163
+ | `claude` | Claude Code |
164
+ | `codex` | Codex |
165
+ | `gemini` | Gemini CLI |
523
166
 
524
- ```
525
- npx -y @cychien/ezdesign@latest login --token <token>
526
- npx -y @cychien/ezdesign@latest inspire --query "a command menu over the whole product"
527
- npx -y @cychien/ezdesign@latest inspire --query "an onboarding checklist" --tag "dark popover" --limit 3
528
- ```
167
+ Without `--agent`, ezdesign connects the agent the project had, else the one you used last, else
168
+ the first of these it finds installed. One you disconnect in the shell stays off for that project.
529
169
 
530
- `login` writes the token to `~/.ezdesign/credentials.json`, mode 600; without `--token` it prompts,
531
- and `logout` forgets it. A search takes a `--query`, `--tag`, or both. `inspire` prints a block per
532
- result - the tags, `Shows:`, `Components:`,
533
- `Actions:`, `Why it works:`, `Wrong when:`, and local paths to the entry's images, which are
534
- downloaded once and reused after that; `(approximate)` in a heading means the entry was read off a
535
- screenshot. `--json` prints the same response with those paths in place of the service's own URLs,
536
- and carries what the block leaves out: the visible wording, the layout and its metrics, and the
537
- relevance score. There is no fixed list of tags; the query does the finding.
170
+ ezdesign talks to agents over the [Agent Client Protocol](https://agentclientprotocol.com). Any
171
+ other value is run as an ACP server command, so a custom agent works too:
172
+ `--agent 'my-acp-agent --flag'`.
538
173
 
539
- Nothing about the collection ships in the package: a query returns what it asked for, and the
540
- results belong in the agent's reply, not in your repository. A search that finds nothing exits 0
541
- with one line, and one that cannot run - no token, a refused token, a service that cannot be
542
- reached - exits 2 with one line, so the agent says so and carries on.
174
+ ## Canvases
543
175
 
544
- ## Saving references from Chrome
176
+ A canvas is an infinite board of frames. Each frame is a complete HTML page at a real viewport width,
177
+ so a 390 frame is a phone to its media queries however far you zoom out.
545
178
 
546
- The capture extension saves an image, a video or a region of any page into one design library
547
- under `~/.ezdesign/library/`, shared by every project, with your tags and a note. Saving needs no
548
- dev server, no session and no daemon: Chrome talks to a small native host that ezdesign installs.
179
+ - **Open one** from the `+` in the tab strip, or run ezdesign without a url to work on canvases in
180
+ a project that has no dev server yet:
181
+ ```sh
182
+ npx -y @cychien/ezdesign@latest --agent claude
183
+ ```
184
+ - **Ask for designs** and the agent adds them as frames. Annotate a frame exactly like your app.
185
+ - **Move around** by dragging the background or scrolling. Pinch or `⌘`-scroll to zoom, `⇧1` to fit
186
+ everything, `⇧0` for 100%. Frames snap to each other, and `⌘Z` undoes.
187
+ - **Bring one into the app** with `/element`: comment on your app, switch to the canvas, `⌘`-click
188
+ the frame you mean.
549
189
 
550
- ```
190
+ Canvases are saved in your project under `.ezdesign/canvases/`. Commit them or ignore them, your
191
+ call. Frames are served from a separate origin, so agent-written pages cannot reach the review shell.
192
+
193
+ ## Design library
194
+
195
+ The Chrome extension saves references into one library under `~/.ezdesign/library/`, shared by
196
+ every project. Saving works anytime, with or without a review open.
197
+
198
+ ```sh
551
199
  npx -y @cychien/ezdesign@latest extension install
552
200
  ```
553
201
 
554
- `install` copies the host into `~/.ezdesign/native-host/`, registers it with every Chromium browser
555
- it finds (Chrome and its channels, Chromium, Arc, Brave, Edge, Vivaldi; macOS and Linux), and
556
- prints the folder to load once: open `chrome://extensions`, turn on **Developer mode**, choose
557
- **Load unpacked** and pick `~/.ezdesign/extension`. The folder stays put across ezdesign updates,
558
- and the daemon brings the host and the extension up to its own version when it starts. The host
559
- needs Node 22.13 or newer. `--user-data-dir <dir>` installs into one browser profile only.
560
-
561
- - **Right-click an image or a video** and choose **儲存至 ezdesign**. A dialog opens over the page
562
- with a preview, tags (suggested by how often you use them) and a note; **儲存** or Cmd+Enter saves,
563
- Esc or a click outside leaves nothing behind. Saving the same bytes again shows **已收藏過** and
564
- adds to the item you already have.
565
- - **The toolbar icon or Alt+Shift+S** starts a region capture: drag a rectangle, or click once for
566
- the whole visible page. On pages an extension cannot draw on, such as `chrome://` pages, the
567
- visible page is captured at once and the dialog opens in its own window.
568
- - Images and plain video files keep their original bytes. A streaming video (YouTube, Instagram,
569
- anything playing through MediaSource) and a video over 200 MB are saved as a link with the time
570
- you were at and the frame on screen. An image the host is refused for (a signed-in CDN) offers
571
- **允許讀取** for that site, then is read with your cookies.
572
-
573
- `ezdesign extension status` lists each browser and any problem, and exits 1 when there is one.
574
- `ezdesign extension uninstall` removes the host and the extension folder; the library stays.
575
-
576
- ## Signing in
577
-
578
- Some features will run on the ezdesign backend, and those need you signed in. The icon at the foot of the review shell's right-hand bar then signs you in, with Google or an
579
- email and a password. It opens the sign-in page in a new tab. When you are through, the tab closes
580
- itself and the icon shows your initial. Its menu shows the account and signs you out. One sign-in
581
- covers every review the daemon is serving. The token is kept in `~/.ezdesign/credentials.json`,
582
- mode 600, beside the inspiration token, and signing out revokes it.
202
+ Then open `chrome://extensions`, turn on **Developer mode**, choose **Load unpacked** and pick
203
+ `~/.ezdesign/extension`. Works with Chrome, Chromium, Arc, Brave, Edge and Vivaldi on macOS and
204
+ Linux.
583
205
 
584
- ## Development
206
+ - **Right-click any image or video** and save it to ezdesign.
207
+ - **Press `Alt+Shift+S`** (or click the toolbar icon) and drag over any part of a page.
208
+ - Add tags and a note on why it is good, so the agent knows how to use it later.
209
+
210
+ `ezdesign extension status` checks the install, and `ezdesign extension uninstall` removes it while
211
+ keeping your library.
585
212
 
586
- Working on ezdesign itself takes one command:
213
+ ## Sessions and updates
214
+
215
+ A review belongs to your project (the nearest `.git` or `package.json`), not to a port. Restart
216
+ your dev server on another port and run ezdesign again: the shell switches over and the
217
+ conversation and queue carry on.
218
+
219
+ Reviews survive restarts. The background daemon brings its sessions back on the ports they held,
220
+ your open shell reconnects on its own, and the agent resumes the conversation it was in.
221
+
222
+ When a newer version is out, a card at the top of the shell offers it. One click installs it and
223
+ hands your sessions over, and if the new version fails to start you stay on the one you had.
224
+ Always run the CLI as `npx -y @cychien/ezdesign@latest`; a bare `npx @cychien/ezdesign` keeps
225
+ using whatever version npx cached. Update the skill with `npx skills update ezdesign`.
226
+
227
+ ## CLI
587
228
 
588
229
  ```
589
- npm run dev # watch src/, serve the playground fixture, start an isolated daemon,
590
- # open the review shell with an ACP agent on it
591
- npm run dev -- --no-plugin # the same page without ezdesignSource(), to see fallback-only anchors
592
- npm run dev:agent # a stand-in poll-mode agent, for working on the poll contract itself
593
- npm run dev:cli status # the ezdesign CLI, pointed at the dev daemon instead of the real one
594
- npm run brand # regenerate the logo's PNG and SVG copies after editing src/brand.ts
230
+ ezdesign [<url>] [--agent <name>] open or resume this project's review
231
+ ezdesign status show running sessions
232
+ ezdesign stop stop the background daemon
233
+ ezdesign extension install set up the Chrome extension
234
+ ezdesign --help everything else
595
235
  ```
596
236
 
597
- `npm run dev` opens the review in ACP mode, scoped to the fixture - the agent's working directory
598
- is `fixtures/playground`, which is the project it is reviewing. Nothing else is needed; `dev:agent`
599
- is only for the poll contract.
237
+ `ezdesign` here is short for `npx -y @cychien/ezdesign@latest`.
238
+
239
+ ## Configuration
240
+
241
+ | Variable | Default | Effect |
242
+ | --- | --- | --- |
243
+ | `EZDESIGN_DATA_DIR` | `~/.ezdesign` | Where sessions, the daemon's registry and log, and installed versions live. A separate dir runs a fully isolated instance. |
244
+ | `EZDESIGN_CONTROL_PORT` | `4400` | First of the ten ports the daemon tries for its control server. |
245
+ | `EZDESIGN_NO_UPDATE_CHECK` | unset | `1` never asks npm for a newer version. |
246
+ | `EZDESIGN_NO_IDLE_STOP` | unset | `1` keeps the daemon running when idle. By default it stops after 30 minutes with no shell open. |
247
+
248
+ ## Privacy
249
+
250
+ The daemon binds to `127.0.0.1` and your code stays on your machine. Apart from your agent's own
251
+ traffic, ezdesign reaches out only to npm, to fetch the agent adapter and to check for a newer
252
+ version (at most every four hours, off with `EZDESIGN_NO_UPDATE_CHECK=1`), and to the ezdesign
253
+ backend for sign-in and inspiration search when you use them.
254
+
255
+ ## Development
256
+
257
+ ```sh
258
+ npm run dev # watch src/, serve the playground fixture, open a review with an agent
259
+ npm run dev -- --no-plugin # the same, without the Vite plugin, to see fallback anchors
260
+ npm run dev:cli status # the CLI, pointed at the dev daemon
261
+ npm run brand # regenerate logo assets after editing src/brand.ts
262
+ npm run check # build, typecheck, test
263
+ ```
600
264
 
601
- A client rebuild (`src/client/**`) only needs a browser reload; a rebuild the daemon loads
602
- restarts it, and in-flight polls reconnect on their own. Dev mode keeps its daemon registry and
603
- sessions in `.dev/`, so your own `~/.ezdesign` is never touched. Each checkout runs its own stack:
604
- its daemon takes a control range in 4100-4199 and the fixture a port from 5173, both picked from
605
- the checkout's path and recorded in `.dev/dev.json`, so worktrees run `npm run dev` side by side
606
- and `npm run dev:cli` reaches its own checkout's daemon. The review target is
265
+ Dev mode keeps its state in `.dev/`, so your own `~/.ezdesign` is never touched, and each checkout
266
+ picks its own ports, so worktrees run side by side. The review target is
607
267
  [`fixtures/playground`](fixtures/playground/README.md).
608
268
 
609
269
  ## Status
610
270
 
611
- Early. On the roadmap: diff-derived Keep/Undo and layout-issue detection. ACP mode is an
612
- experimental spike.
271
+ Early and moving fast. Feedback and issues are welcome.
613
272
 
614
273
  ## License
615
274