@marver-design/marver 0.8.0 → 0.9.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 (52) hide show
  1. package/CHANGELOG.md +168 -5
  2. package/README.md +73 -16
  3. package/dist/{auth-B36fMCM3.mjs → auth-KQ9Aj-nB.mjs} +1 -1
  4. package/dist/{build-B8z902TW.mjs → build-D9gimz6K.mjs} +5 -5
  5. package/dist/cli.mjs +16 -7
  6. package/dist/{collab-pLGzSvm5.mjs → collab-s3k5byM1.mjs} +3 -3
  7. package/dist/{comments-DSwMhdbZ.mjs → comments-BZBKhKRO.mjs} +2 -2
  8. package/dist/{comments-BrpC86Kf.mjs → comments-J06jqCVV.mjs} +3 -3
  9. package/dist/{daemon-C-huGHAM.mjs → daemon-2Lft_lVV.mjs} +60 -66
  10. package/dist/{dev-BYm9ebfN.mjs → dev-CTtkqVo_.mjs} +21 -7
  11. package/dist/{init-DrLomSWq.mjs → init-8Giknvy1.mjs} +35 -7
  12. package/dist/ledger-BgA7nQoH.mjs +103 -0
  13. package/dist/{manifest-D3eaARf4.mjs → manifest-DJHU7qfu.mjs} +144 -19
  14. package/dist/{plugin-BVFuRfEo.mjs → plugin-BrBWk2Qn.mjs} +41 -9
  15. package/dist/{serve-CwAfayJk.mjs → serve-CZqPnj19.mjs} +32 -8
  16. package/dist/{sync-Ch4Bymb1.mjs → sync-BJKKmy1n.mjs} +2 -2
  17. package/dist/work-CLrmY-vQ.mjs +97 -0
  18. package/dist/work-lzC-lPY0.mjs +76 -0
  19. package/package.json +17 -2
  20. package/src/client/const.ts +1 -1
  21. package/src/client/content/diagram.tsx +1 -1
  22. package/src/client/content/index.tsx +2 -2
  23. package/src/client/content/md.ts +1 -1
  24. package/src/client/content/palette.ts +2 -2
  25. package/src/client/frame-host/bridge.js +1 -1
  26. package/src/client/frame-host/inspect.js +2 -2
  27. package/src/client/frame-host/serialize.ts +2 -2
  28. package/src/client/shell/App.tsx +79 -22
  29. package/src/client/shell/Comments.tsx +24 -18
  30. package/src/client/shell/Play.tsx +2 -2
  31. package/src/client/shell/canvas/Canvas.tsx +1 -1
  32. package/src/client/shell/canvas/FrameNode.tsx +16 -11
  33. package/src/client/shell/canvas/snapshots.ts +3 -3
  34. package/src/client/shell/comments-store.ts +115 -42
  35. package/src/client/shell/hash.ts +2 -2
  36. package/src/client/shell/icons.tsx +1 -1
  37. package/src/client/shell/keys.ts +39 -0
  38. package/src/client/shell/mentions.ts +1 -1
  39. package/src/client/shell/perf.ts +1 -1
  40. package/src/client/shell/store.ts +99 -39
  41. package/src/client/shell/styles.css +16 -16
  42. package/src/client/shell/tidy.ts +5 -5
  43. package/src/client/stage/main.tsx +2 -2
  44. package/src/shared/events.ts +2 -2
  45. package/src/shared/utm.ts +22 -0
  46. package/templates/AGENTS-embedded.md +49 -2
  47. package/templates/AGENTS-studio.md +49 -2
  48. package/templates/instructions/configure.md +6 -1
  49. package/templates/instructions/jam.md +29 -9
  50. package/templates/instructions/publish.md +1 -1
  51. package/templates/instructions/welcome.md +7 -1
  52. package/dist/ledger-wFvEIEGi.mjs +0 -64
package/CHANGELOG.md CHANGED
@@ -2,6 +2,168 @@
2
2
 
3
3
  Notable changes to `@marver-design/marver`. Format follows [Keep a Changelog](https://keepachangelog.com); versions follow semver.
4
4
 
5
+ ## 0.9.0 - 2026-08-21
6
+
7
+ ### Added
8
+
9
+ - **A Live Jam guide** (`docs/live-jam.md`), now that the feature arrives armed rather than
10
+ opted into: how the agent is chosen and how to correct it, every key in the config block,
11
+ what each of the two CLIs is allowed to do, where the trust boundary sits, and what to
12
+ check when a mention does nothing.
13
+
14
+ ### Changed
15
+
16
+ - **Live Jam is on by default, at concurrency 6.** Tagging `@marver` in a comment was the
17
+ headline workflow and a config edit stood in front of it. Now it arms itself: the tool
18
+ RUNNING the process wins (its env markers are evidence, and `init` is usually run by the
19
+ agent), then whatever is on PATH, claude first. That last tie-break is a guess, which is
20
+ why the answer is made visible rather than clever - `init` prints the agent it chose and
21
+ writes it into `design/config.ts` in plain sight as
22
+ `jam: { agent: "claude", concurrency: 6 }`, and the generated instructions have the agent
23
+ confirm that line names the tool it actually is. One word to correct, once per repo.
24
+ Workspaces that predate the block need no re-init; they resolve the same way at every
25
+ dev boot. `jam: false` is the off switch, and `jam: "codex"` is shorthand for naming the
26
+ agent. Six frames at once replaces three - at three, half of a multi-frame ask sat
27
+ waiting on the other half while the human watched. With no agent CLI installed, jam stays
28
+ off and both `init` and `marver dev` say so instead of going quiet.
29
+ - **A named agent is never quietly swapped, nor armed when it cannot run.** `jam.agent`
30
+ naming something marver cannot spawn turns Live Jam off with a printed reason rather than
31
+ detecting some other tool and answering the human's comments with it; the same applies
32
+ when the named CLI is not on PATH, which used to claim every mention and then fail it. A
33
+ `design/config.ts` that fails to parse also leaves jam off - it may have said `jam: false`,
34
+ and arming a process spawn against intent we cannot read is the one wrong-way error worth
35
+ avoiding.
36
+ - **`jam.subagents` does something now, and Codex fans out too.** The setting existed but never
37
+ reached the spawned agent, which reads no config - so the parallel-frame policy is stated in
38
+ the job prompt, and turning it off keeps a job on a single agent. The Codex adapter had also
39
+ been marked as having no subagents; `codex exec` carries `collaboration.spawn_agent`, so a
40
+ multi-frame Codex job now fans out the way a Claude Code one does. The prompt only ever says
41
+ "you MAY", so an older CLI without those tools just works serially instead of failing.
42
+ - **Worth knowing, now that it is on by default:** the two agents are locked down differently,
43
+ because their CLIs differ. Claude Code is spawned with shell access removed entirely
44
+ (`--disallowedTools Bash`); Codex runs in its own `workspace-write` sandbox, which bounds
45
+ what commands can *touch* but still lets the model run them. Both are confined to the
46
+ workspace, and every change is a diff you review.
47
+
48
+ ### Fixed
49
+
50
+ - **A one-message agent no longer posts the raw reply fence into the thread.** Live Jam posts
51
+ the agent's first streamed message as an immediate ack. Codex emits a single message at the
52
+ very end, carrying the completion block, and a fast Claude Code run can do the same - so the
53
+ ack was the finished reply, fence and all, followed by a second message with the same words.
54
+ The early path now normalizes exactly like the final one, which also makes the existing
55
+ duplicate check catch it: one clean reply.
56
+
57
+ - **The jam ledger and journal are bound to the machine that wrote them.** Both live in
58
+ `design/.local/`, which is gitignored and never synced - but gitignore is a convention,
59
+ not provenance: a repo can force-add its own `.local/` and hand a clone a pre-authorized
60
+ ledger plus a pre-baselined journal. Each line and file now carries a device stamp, so
61
+ jam state that arrived with a clone is read as absent. The stamp is derived from the
62
+ machine, not stored (marver writes nothing outside `design/`), so it stops one repo
63
+ published to everyone rather than someone who already knows your machine - and the larger
64
+ caution is unchanged either way: `marver dev` imports and executes `design/config.ts`, so
65
+ running a dev server in a repo you do not trust is already running its code.
66
+ One-time upgrade cost: an existing journal predates the stamp, so the first boot after
67
+ upgrading rebaselines - any `@marver` mention left unprocessed while the server was down
68
+ is marked seen instead of run. Re-comment to pick it up.
69
+
70
+ - **Comments wear the brand blue.** Pins, thread cards, the comment-mode pick cursor and
71
+ the anchored-thread chrome move off systemGreen. Interact keeps purple, and green is now
72
+ reserved for the done state alone - in dark mode the comment and done greens had drifted
73
+ to the same value, so a frame carrying threads and a frame that had just landed a change
74
+ looked alike. A pin, a thread card and a selected frame are told apart by shape.
75
+
76
+ ## 0.8.1 - 2026-08-19
77
+
78
+ ### Added
79
+
80
+ - **`marver work` - the chat agent's hand on the canvas.** The live working shimmer was
81
+ exclusive to Live Jam; now any coding agent can drive it: create the frame files first,
82
+ pin them on the board, `npx marver work start <scene/frame ...>` - the human sees the
83
+ request land on the canvas in seconds, before the first component exists - build
84
+ (independent frames in parallel, one subagent each), then `work done`. Marks are leased
85
+ (default 10 min, max 30) so a crashed agent can never leave a frame glowing. The dev
86
+ server writes `design/.local/dev.json` (port + per-boot token) as the CLI's discovery
87
+ and credential; presence itself never touches disk. The generated AGENTS.md teaches
88
+ the choreography.
89
+ - **`marver canvas` - a second name for `marver dev`.** Both start the same full local
90
+ canvas (hot reload, comments, Live Jam, working state); `dev` reads naturally to
91
+ developers, `canvas` to everyone else. There is no reduced mode behind either name.
92
+ - **Agents report marver bugs upstream.** The generated AGENTS.md now teaches the
93
+ coding agent to file issues on the marver repo when the TOOL itself misbehaves
94
+ (search first, `bug`/`enhancement` labels, version + expected-vs-actual + an
95
+ abstract reproduction) - with privacy as hard law: issues are public, so nothing
96
+ from the owner's repo (code, names, comment text, screenshots) may appear; failures
97
+ are described in neutral terms or handed to the owner instead. The owner is always
98
+ told what was filed. Every canvas becomes a field reporter, no user effort.
99
+
100
+ ### Changed
101
+
102
+ - **The powered-by links carry automatic attribution.** Every canvas's marver.design link
103
+ now ships UTM-tagged with zero setup: `utm_source` says which surface class sent the
104
+ visitor (`published-canvas` / `dev-canvas`), `utm_medium=powered-by`, `utm_campaign` is
105
+ the canvas's own name slugged (`marver-tour`), and `utm_content` names the placement
106
+ (`gate` badge / `shell` wordmark).
107
+ - **`init` wires every agent in, and showing the work comes first.** The repo-root
108
+ CLAUDE.md @-import has a sibling: init now also creates (or appends one pointer line
109
+ to) a root AGENTS.md, so Codex-style agents inherit the canvas contract too. And the
110
+ contract's Show-the-work section is stricter - making the request visible is the
111
+ agent's FIRST act: skeleton frame created, pinned, and shimmering before research,
112
+ reading the codebase, or planning begins.
113
+ - **Comments are frame-scoped now, and notifications reach you anywhere.** The client holds
114
+ every board's comment log, not just the open board's: a thread pins to its frame wherever
115
+ that frame appears - the all-scenes board finally shows every conversation - and a Marver
116
+ reply landing on ANY board raises the notification pill no matter where you are, with View
117
+ navigating to the right board and opening the thread. Replies and resolves route to the
118
+ thread's origin log, so reading a thread from another board never forks it.
119
+
120
+ ### Fixed
121
+
122
+ - **The jam shell ban is enforced, not just requested.** The Claude Code adapter now passes
123
+ `--disallowedTools Bash` alongside its tool allowlist: `--allowedTools` only pre-approves
124
+ tools, so a permissive inherited `settings.json` could have re-authorized shell. The
125
+ documented boundary (no shell for untrusted comment text) is now explicit in the spawn.
126
+ - **The jam ack never narrates.** The agent's streamed first line posts to the thread
127
+ verbatim - and sometimes that line was plan narration ("I'll start by acknowledging,
128
+ then look at the board...") instead of a message to the owner. The packet now spells
129
+ out that the first text ships verbatim and must address the owner, and the daemon
130
+ backstops it: an unmistakable plan-narration first line is skipped and the next
131
+ streamed text becomes the ack - a later tight ack, never a leaked plan.
132
+ - **`data-goto` follows a frame to its board.** Clicking a link whose target frame lives on
133
+ another board used to SPAWN that frame onto the current board (mutating it - and in dev,
134
+ saving the mutation). Navigation now follows the frame home: the canvas switches to the
135
+ first curated board that pins the target, focuses it, and carries interact mode across the
136
+ switch - links are navigation, never edits. A frame no board pins still spawns in place
137
+ (the original single-board prototype behavior), and play mode is unchanged.
138
+ - **Comments no longer vanish across board switches and reloads.** Board nodes the file did not
139
+ key yet were minted RANDOM keys on every load - and comments anchor to node keys, so any
140
+ comment created on a never-saved board orphaned invisibly on the next mount (and on published
141
+ canvases, which can never save keys back, on every visit). Keys are now deterministic
142
+ (board + frame + occurrence), and a single thread-host resolver adopts threads whose stored
143
+ key no longer holds onto the first node still showing their frame - a comment degrades to its
144
+ frame, never to invisible, and never to two pins.
145
+ - **Old comment logs keep their origin.** Events written by 0.8.0 clients carry no `board`;
146
+ they now get it stamped from the log they came from, so replying to an old thread can never
147
+ route the reply into whatever board happens to be open.
148
+ - **Create-first frames land at their declared size.** A board node appended before the
149
+ manifest registered its frame file was sized by the 390x844 guess - and the guess persisted.
150
+ The size now corrects itself the moment the frame arrives, guessed sizes never reach the
151
+ file, and authored sizes on temporarily-missing frames are never touched.
152
+ - **`marver init` no longer resurrects the demo scene.** Re-running init on a workspace with
153
+ real scenes used to scaffold demo/ back next to real work; the demo now only lands on a
154
+ first canvas.
155
+ - **Static canvases stop knocking.** A published canvas without collaboration used to poll
156
+ the comments API forever (and retry an EventSource against it); the client now detects the
157
+ static serve once and goes quiet.
158
+ - **No phantom comments on static canvases.** A published serve without `MARVER_DATA_DIR` used to
159
+ let `/__mv/api/*` requests fall through to the static handler, which answered them with
160
+ `index.html` HTTP 200 - the client read that as success, so a guest's comment echoed locally
161
+ and silently evaporated on reload. The serve now refuses the API with a 404 JSON error the
162
+ client surfaces as a toast.
163
+ - **The published shell wordmark honors `share.name`.** Deploy hosts build from anonymous paths
164
+ (`/app`), so the sidebar read "App"; a declared `share.name` now names the shell like it
165
+ already named the gate.
166
+
5
167
  ## 0.8.0 - 2026-08-19
6
168
 
7
169
  Live Jam: tag `@marver` in a canvas comment and a local coding agent picks it up, edits the real
@@ -20,8 +182,9 @@ on the comment thread experience.
20
182
  Every reply carries a provenance tooltip - dev user, harness, model.
21
183
  - **A hard trust boundary.** Only the owner's device triggers: a device-bound ledger (keyed per
22
184
  board + comment id, never synced), a same-origin + cookie gate on every dev write (comments AND
23
- profile), and a recursion guard so agent replies never re-trigger. The agent gets no shell -
24
- Read/Edit/Write/Glob/Grep + WebSearch/WebFetch only - and its reply is extracted structurally
185
+ profile), and a recursion guard so agent replies never re-trigger. The agent runs locked down
186
+ (Claude Code: no shell, Read/Edit/Write/Glob/Grep + WebSearch/WebFetch only; Codex: confined
187
+ to its workspace-write sandbox) - and its reply is extracted structurally
25
188
  (the `marver-reply` block), so narration can never leak into a thread. Comment text is framed
26
189
  as untrusted data end to end.
27
190
  - **The felt surface.** A frame being worked on wears the selection geometry in marver blue with
@@ -177,8 +340,8 @@ snapshot that IS what you see, and the real live app takes over the moment you i
177
340
 
178
341
  - **Sidebar header** shows the humanized repo name (`marver-pilot` → "Marver Pilot", ellipsed if
179
342
  long); the logo links to marver.design.
180
- - **Sidebar board/scene labels** are humanized - kebab filenames render Title Case (`tms-specs` →
181
- "Tms Specs"), dropping the dashes, while an explicit `meta.title` is honored verbatim.
343
+ - **Sidebar board/scene labels** are humanized - kebab filenames render Title Case (`crm-specs` →
344
+ "Crm Specs"), dropping the dashes, while an explicit `meta.title` is honored verbatim.
182
345
  - **App cursor** is the marver arrowhead - tilted, rounded, small, soft-shadowed, and theme-adaptive
183
346
  (black-on-light / white-on-dark); reverts to a normal pointer in interact/prototype and keeps the
184
347
  pin/crosshair in comment/laser mode.
@@ -328,7 +491,7 @@ The co-thinking release: the canvas now holds the thinking, not just the screens
328
491
 
329
492
  ## 0.2.3 - 2026-08-12
330
493
 
331
- - Hardening release: codex P1s (live-JOIN adjacency, same-directory group invariant, tsx-only inference) and a P2 sweep (extractor boundaries, sceneRows dedupe, play-mode chrome fixes, extreme-zoom badge fade).
494
+ - Hardening release: live-JOIN adjacency, same-directory group invariant, tsx-only inference, extractor boundaries, sceneRows dedupe, play-mode chrome fixes, extreme-zoom badge fade.
332
495
 
333
496
  ## 0.2.2 - 2026-08-12
334
497
 
package/README.md CHANGED
@@ -1,32 +1,69 @@
1
1
  # Marver
2
2
 
3
- The agent-native design canvas. A `design/` folder in your repo, one command, and a canvas of live frames built from your app's real components and theme. Your coding agent designs by writing files; the tool ships no AI.
3
+ [![npm](https://img.shields.io/npm/v/%40marver-design%2Fmarver?color=2f6fed&label=npm)](https://www.npmjs.com/package/@marver-design/marver)
4
+ [![license](https://img.shields.io/badge/license-Apache--2.0-green)](LICENSE)
5
+ [![node](https://img.shields.io/badge/node-%3E%3D22.18-brightgreen)](package.json)
6
+
7
+ **The agent-native design canvas.** A `design/` folder in your repo, one command, and a canvas of live frames built from your app's real components and theme. Your coding agent designs by writing files; the tool ships no AI.
8
+
9
+ [marver.design](https://marver.design) · [Live Jam](docs/live-jam.md) · [Deploying a canvas](docs/publish.md) · [Changelog](CHANGELOG.md) · [Contributing](CONTRIBUTING.md) · [Issues](https://github.com/TNEP4/marver/issues)
10
+
11
+ ## Quickstart
4
12
 
5
13
  ```bash
6
14
  npm i -D @marver-design/marver
7
15
  npx marver init # scaffolds design/ (detects shadcn, Tailwind, your router)
8
- npx marver dev # canvas at localhost:5199
16
+ npx marver dev # canvas at localhost:5199 (npx marver canvas works too - same thing)
9
17
  ```
10
18
 
11
19
  Then, to your agent:
12
20
 
13
21
  > Read design/AGENTS.md. Build an onboarding scene - welcome, form, done - mobile-first, using our components.
14
22
 
15
- - **Frames** are plain TSX/HTML files - zero imports from this package required.
16
- - **Everything hot-reloads**; frames appear on the canvas the moment the file lands.
17
- - Drag a frame's edge and the real breakpoints fire - each frame is a true iframe viewport.
18
- - **Boards**: one canvas on screen at a time. Agents write `design/boards/<name>.json` (a frame list is enough); switch boards at the top of the sidebar. `all-scenes` is auto-managed.
19
- - **Devices view**: the Devices menu (or hotkeys `1`-`5`) sizes every frame to mobile / tablet / laptop / monitor / tv to sweep your breakpoints; `0` restores your own layout exactly. Widths live in `design/config.ts`.
20
- - `data-goto="scene/frame"` on any element links frames into a walkable prototype.
21
- - **Content frames**: specs, Mermaid diagrams, and mood boards live on the same canvas as the screens - import `Doc`, `Md`, `Diagram`, `Img` from `@marver-design/marver/content` and think a feature through *before* any pixels exist. Diagrams ship pre-themed (both modes), content frames auto-size to their content, and everything - devices, play mode, publish - works on them identically. Works in a repo with no app at all: idea first, design second.
22
- - **Comments**: Google-Docs-style feedback, pinned to actual elements. Press `C`, click a div inside a frame, write - the thread lives on that element, survives edits via a layered anchor (source semantics → structure → fuzzy text), and collapses to an avatar stack when the frame isn't active. Viewers on a published canvas comment with real names and avatars (invite-link accounts, no email infrastructure); `marver dev` syncs the same threads into `design/comments/*.jsonl`, where your agent works the queue: `npx marver comments list --open --json` → fork a variant → `resolve --addressed-in`. Live via SSE; one deploy, no extra services (set `MARVER_DATA_DIR` on a volume + `MARVER_OWNER_EMAIL` for the first account).
23
- - **Laser mode**: `L` outlines every element in every frame with depth-hued borders plus a hover label - the fastest way to see structure. Click any element to copy its full address (frame file + CSS path) for the agent. Comment mode is the calm cousin - one at a time with laser - showing only the hovered element so picking a comment target never overwhelms.
24
- - **Upgrade**: `npm i -D @marver-design/marver@latest && npx marver init`. The canvas tells you when a new version is out (one anonymous registry check per day, cached in `design/.local/`; `MARVER_NO_UPDATE_CHECK=1` disables). Re-running init refreshes the managed files (AGENTS.md, `design/instructions/`) - your edits to them are detected and preserved; when both you and a release changed a file, the fresh version is staged at `design/.local/latest/` for you (or your agent) to merge. Everything else in `design/` is yours and never touched.
25
- - Uninstall: delete `design/`, remove the dependency. (If `init` patched your tsconfig `exclude`, revert that one line.)
23
+ Frames appear on the canvas the moment the files land. That's the loop.
24
+
25
+ ## Why marver
26
+
27
+ - **Frames are real code.** Plain TSX/HTML files rendered from your repo's actual components and theme - zero imports from this package required. An approved design promotes into the app by moving a file, not by re-implementing a picture.
28
+ - **Everything hot-reloads.** The agent writes, you watch it land - live.
29
+ - **True viewports.** Each frame is a real iframe: drag its edge and your actual breakpoints fire.
30
+ - **Your agent answers on the canvas.** Tag `@marver` in a comment and it picks up the job, edits the real source, and replies in the thread - no wiring, on by default. See [Live Jam](#live-jam).
31
+ - **No AI inside.** The designer is the coding agent you already run and pay for. `init` generates the `design/AGENTS.md` contract that teaches it the whole workflow.
32
+
33
+ ## The canvas
34
+
35
+ - **Frames, scenes, boards.** Frames are screens, scenes group them (`design/scenes/<scene>/<frame>.tsx`), boards arrange them. Agents write `design/boards/<name>.json` (a frame list is enough); switch boards at the top of the sidebar. `all-scenes` is auto-managed.
36
+ - **Devices view.** Hotkeys `1`-`5` (or the Devices menu) size every frame to mobile / tablet / laptop / monitor / tv to sweep your breakpoints; `0` restores your own layout exactly. Widths live in `design/config.ts`.
37
+ - **Prototype links.** `data-goto="scene/frame"` on any element links frames into a walkable prototype - across boards, too.
38
+ - **Play mode.** Press `p`: the board becomes a full-screen, clickable walkthrough. `data-goto` links navigate, arrow keys step between frames, `[` / `]` cycle variants, `Escape` exits. Publish it and you have a shareable prototype.
39
+ - **Content frames.** Specs, Mermaid diagrams, and mood boards live on the same canvas as the screens - import `Doc`, `Md`, `Diagram`, `Img` from `@marver-design/marver/content` and think a feature through before any pixels exist. Works in a repo with no app at all: idea first, design second.
40
+
41
+ ## Collaboration
42
+
43
+ - **Comments.** Google-Docs-style feedback pinned to actual elements. Press `c`, click a div inside a frame, write - the thread lives on that element and survives edits via a layered anchor (source semantics → structure → fuzzy text). Viewers on a published canvas comment with real names and avatars (invite-link accounts, no email infrastructure). `marver dev` syncs the same threads into `design/comments/*.jsonl`, where your agent works the queue: `npx marver comments list --open --json` → fork a variant → `resolve --addressed-in`. Live via SSE; one deploy, no extra services.
44
+ - **Laser mode.** Press `l`: every element in every frame gets depth-hued outlines plus a hover label - the fastest way to see structure. Click any element to copy its full address (frame file + CSS path) for the agent.
45
+ - **Publishing.** `npx marver build` exports a static canvas (default-closed: `design/publish.json` names what ships); `npx marver serve` hosts it with an optional password gate. One deploy on Railway, Docker, or any static host - the [publishing guide](docs/publish.md) has the one-pagers.
46
+
47
+ ## Live Jam
48
+
49
+ Tag `@marver` in a comment and your own coding agent picks it up - reads the thread, edits the real frame source, replies with a receipt - while the frame wears a live working glow. Nothing to start and nothing to wire: it rides along with `marver dev`, on by default, armed with whichever agent CLI you have. The tool running the process wins, then whatever is on PATH, and `init` writes what it found into `design/config.ts` as `jam: { agent: "claude", concurrency: 6 }` - visible, one word to correct, `jam: false` to switch off.
50
+
51
+ The trust boundary is hard: only comments written on the owner's machine trigger (a device-bound ledger - a drive-by comment on a published canvas cannot start work), the agent runs locked down (Claude Code with shell disabled entirely; Codex confined to its workspace-write sandbox), and every reply carries provenance: which agent ran it, as which dev user, on which model when the agent names one. Marver ships no AI; the agent that acts is the one you already run. The [Live Jam guide](docs/live-jam.md) has the config block, the two sandboxes, and what to check when a mention does nothing.
26
52
 
27
- **Next.js**: supported with one caveat - frames render in Vite, outside Next. `next/font` CSS variables are undefined inside frames (give font tokens a fallback chain), `next/image`/`next/link` should be plain `img`/`data-goto` in frames, and Server Components cannot run there. `init` writes the specifics into `design/AGENTS.md` when it detects Next.
53
+ ## Working state
28
54
 
29
- Implementation contracts (SPEC.md, DECISIONS.md) live in the [GitHub repo](https://github.com/TNEP4/marver).
55
+ The same glow, driven from the terminal. When your agent takes a request, it creates the frame files first, pins them on a board, and runs `npx marver work start <scene/frame ...>` - you see the work land on the canvas in seconds, watch it shimmer while subagents build in parallel, and see it settle on `work done`. Marks self-expire, so a crashed agent never leaves a frame glowing.
56
+
57
+ ## Commands
58
+
59
+ | Command | What it does |
60
+ |---|---|
61
+ | `npx marver init` | Scaffold `design/` in this repo (safe to re-run; refreshes managed files) |
62
+ | `npx marver dev` / `canvas` | Start the local canvas - hot reload, comments, Live Jam armed (`--port`, default 5199) |
63
+ | `npx marver build` | Static export → `design/.dist`; what ships comes from `design/publish.json` (default-closed) |
64
+ | `npx marver serve` | Serve the export; `MARVER_PASSWORD` gates it, `MARVER_DATA_DIR` persists comments + accounts |
65
+ | `npx marver comments …` | The agent's queue: `connect <url>` · `sync` · `list` · `reply` · `resolve` · `invite <email>` · `revoke <email>` |
66
+ | `npx marver work …` | Working glow from the terminal: `start <scene/frame …>` · `done … \| --all` · `list` |
30
67
 
31
68
  ## Shortcuts
32
69
 
@@ -39,4 +76,24 @@ Implementation contracts (SPEC.md, DECISIONS.md) live in the [GitHub repo](https
39
76
 
40
77
  **Board & chrome** - `t` tidy · `d` toggle light/dark for the board · `⌘\` (ctrl+\) collapse/open sidebar.
41
78
 
42
- **Selection** - click selects · shift+click (canvas or sidebar) builds a multi-selection · `⌘A` selects every frame on the board · `c` copies the selected frames' file paths · double-click enters interact mode (`esc` or click outside leaves) · drag the title bar to move, edges to resize (widths snap to devices).
79
+ **Selection** - click selects · shift+click (canvas or sidebar) builds a multi-selection · `⌘A` selects every frame on the board · `⇧P` copies the selected frames' file paths · double-click enters interact mode (`esc` or click outside leaves) · drag the title bar to move, edges to resize (widths snap to devices).
80
+
81
+ **Modes** - `c` comment mode · `l` laser mode · `⇧C` hide/show comment pins · `⇧L` laser comment (spotlight a thread's element) · `p` play mode · `h` hide all chrome.
82
+
83
+ ## Notes
84
+
85
+ - **Next.js**: supported with one caveat - frames render in Vite, outside Next. `next/font` CSS variables are undefined inside frames (give font tokens a fallback chain), `next/image`/`next/link` should be plain `img`/`data-goto` in frames, and Server Components cannot run there. `init` writes the specifics into `design/AGENTS.md` when it detects Next.
86
+ - **Upgrade**: `npm i -D @marver-design/marver@latest && npx marver init`. The canvas tells you when a new version is out (one anonymous registry check per day, cached in `design/.local/`; `MARVER_NO_UPDATE_CHECK=1` disables). Re-running init refreshes the managed files (AGENTS.md, `design/instructions/`) - your edits to them are detected and preserved; when both you and a release changed a file, the fresh version is staged at `design/.local/latest/` for you (or your agent) to merge. Everything else in `design/` is yours and never touched.
87
+ - **Uninstall**: delete `design/`, remove the dependency. (If `init` patched your tsconfig `exclude`, revert that one line.)
88
+
89
+ ## Status
90
+
91
+ Marver is a young solo side project - it works, it's dogfooded daily, and it has rough edges. The known weak spot: boards with many heavy frames (animation-rich, component-dense) can strain the canvas, especially while zooming - snapshot-based rendering helps but isn't finished. If you hit a wall, an issue with your board shape and frame count genuinely helps.
92
+
93
+ ## Contributing
94
+
95
+ Bug reports, ideas, and PRs are welcome - start with [CONTRIBUTING.md](CONTRIBUTING.md). Security reports go through [private vulnerability reporting](SECURITY.md), not public issues. Agents running marver are taught to file what they hit as issues here, too - expect some robot reporters.
96
+
97
+ ## License
98
+
99
+ [Apache-2.0](LICENSE) · built by [Nic Touron](https://github.com/TNEP4)
@@ -3,7 +3,7 @@ import { dirname, join } from "node:path";
3
3
  import { createHash, randomBytes, scryptSync, timingSafeEqual } from "node:crypto";
4
4
  //#region src/server/auth.ts
5
5
  /**
6
- * Accounts, invites, sessions (SPEC-M3 §3) - the minimal credible implementation,
6
+ * Accounts, invites, sessions - the minimal credible implementation,
7
7
  * extending the gate's own idiom. No framework: per-user salted scrypt verifiers,
8
8
  * opaque session tokens stored hashed, single-use invite links as the identity
9
9
  * bootstrap (no email infrastructure anywhere).
@@ -1,6 +1,6 @@
1
1
  import { i as ROUTE, n as NAME } from "./cli.mjs";
2
- import { o as loadConfig, r as scanFrames, s as detectHost } from "./manifest-D3eaARf4.mjs";
3
- import { n as tailwind3Css, r as tailwind4Plugin, t as marverPlugin } from "./plugin-BVFuRfEo.mjs";
2
+ import { c as detectHost, o as loadConfig, r as scanFrames } from "./manifest-DJHU7qfu.mjs";
3
+ import { n as tailwind3Css, r as tailwind4Plugin, t as marverPlugin } from "./plugin-BrBWk2Qn.mjs";
4
4
  import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync } from "node:fs";
5
5
  import { basename, dirname, join, sep } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
@@ -8,7 +8,7 @@ import { build } from "vite";
8
8
  import react from "@vitejs/plugin-react";
9
9
  //#region src/server/build.ts
10
10
  /**
11
- * `marver build` - the static adapter (SPEC.md §12 + SPEC-M2 §4a).
11
+ * `marver build` - the static adapter.
12
12
  *
13
13
  * Same Vite pipeline as dev (theme wrapper, tailwind, virtuals) with three JS entries
14
14
  * (shell / frame-host / stage); the html pages are generated from the build output, so
@@ -24,7 +24,7 @@ const posix = (p) => p.split(sep).join("/");
24
24
  function packageDir() {
25
25
  return join(dirname(fileURLToPath(import.meta.url)), "..");
26
26
  }
27
- /** Static asset references in one module's source (SPEC-026): <Img src="..."> string
27
+ /** Static asset references in one module's source: <Img src="..."> string
28
28
  * literals plus markdown image literals INSIDE TEMPLATE STRINGS (where Md content
29
29
  * lives - prose in comments never counts). Comments are stripped first so a commented
30
30
  * example cannot fail the build. A computed <Img src={...}> fails CLOSED - the build
@@ -43,7 +43,7 @@ function scanAssetRefs(src, moduleId) {
43
43
  }
44
44
  /** Same shape the client's assetUrl accepts: relative, inside design/assets/, no tricks. */
45
45
  const isLocalAssetRef = (p) => !!p && !p.includes(":") && !p.startsWith("/") && !p.startsWith("\\") && !p.split("/").some((s) => s === ".." || s === "");
46
- /** The publish policy (SPEC-M3 §4): design/publish.json names what ships and with what
46
+ /** The publish policy: design/publish.json names what ships and with what
47
47
  * rights. Publishing is default-CLOSED - no policy and no explicit flag = no build.
48
48
  * Returns name -> 'read' | 'comment'. Boards absent from the result do not ship. */
49
49
  function resolvePublish(root, allBoards, boardsFlag, allBoardsFlag) {
package/dist/cli.mjs CHANGED
@@ -9,7 +9,7 @@ import { fileURLToPath } from "node:url";
9
9
  const NAME = "marver";
10
10
  const PKG = "@marver-design/marver";
11
11
  const ROUTE = "/__mv";
12
- /** Content-frame natural widths (SPEC-026): Doc layout -> own-size width.
12
+ /** Content-frame natural widths: Doc layout -> own-size width.
13
13
  * Shared by the Doc primitive (measurement messages) and the server-side
14
14
  * manifest scan (defaultSize for content frames) - one source, no drift. */
15
15
  const CONTENT_WIDTH = {
@@ -39,14 +39,14 @@ function version() {
39
39
  }
40
40
  const cli = cac(NAME);
41
41
  cli.command("init", "Scaffold design/ in this repo").option("--mode <mode>", "studio | embedded", { default: "studio" }).option("--no-demo", "Skip the demo scene (the demo ships unless this flag is passed)").option("--root <dir>", "Host repo root", { default: "." }).action(async (opts) => {
42
- const { init } = await import("./init-DrLomSWq.mjs");
42
+ const { init } = await import("./init-8Giknvy1.mjs");
43
43
  init(resolve(opts.root), {
44
44
  mode: opts.mode === "embedded" ? "embedded" : "studio",
45
45
  demo: opts.demo !== false
46
46
  });
47
47
  });
48
- cli.command("dev", "Start the canvas").option("--root <dir>", "Host repo root", { default: "." }).option("--port <port>", "Port (default 5199)").action(async (opts) => {
49
- const { dev } = await import("./dev-BYm9ebfN.mjs");
48
+ for (const [name, desc] of [["dev", "Start the local canvas (everything on: hot reload, comments, Live Jam)"], ["canvas", "Start the local canvas - same as dev"]]) cli.command(name, desc).option("--root <dir>", "Host repo root", { default: "." }).option("--port <port>", "Port (default 5199)").action(async (opts) => {
49
+ const { dev } = await import("./dev-CTtkqVo_.mjs");
50
50
  let port;
51
51
  if (opts.port !== void 0) {
52
52
  const n = Number(opts.port);
@@ -56,7 +56,7 @@ cli.command("dev", "Start the canvas").option("--root <dir>", "Host repo root",
56
56
  await dev(resolve(opts.root), port);
57
57
  });
58
58
  cli.command("build", "Static export → design/.dist (what ships comes from design/publish.json - publishing is default-closed)").option("--boards <names>", "Publish only these boards (comma-separated); overrides the publish policy").option("--all-boards", "Publish every board - the loud override for the default-closed policy").option("--root <dir>", "Host repo root", { default: "." }).action(async (opts) => {
59
- const { buildSite } = await import("./build-B8z902TW.mjs");
59
+ const { buildSite } = await import("./build-D9gimz6K.mjs");
60
60
  try {
61
61
  const boards = opts.boards === void 0 ? void 0 : typeof opts.boards === "string" ? opts.boards : "";
62
62
  await buildSite(resolve(opts.root), boards, opts.allBoards === true);
@@ -66,7 +66,7 @@ cli.command("build", "Static export → design/.dist (what ships comes from desi
66
66
  }
67
67
  });
68
68
  cli.command("serve", "Serve design/.dist (set MARVER_PASSWORD to gate it)").option("--root <dir>", "Host repo root", { default: "." }).option("--port <port>", "Port (default $PORT or 4199)").action(async (opts) => {
69
- const { serve } = await import("./serve-CwAfayJk.mjs");
69
+ const { serve } = await import("./serve-CZqPnj19.mjs");
70
70
  let port;
71
71
  if (opts.port !== void 0) {
72
72
  const n = Number(opts.port);
@@ -75,7 +75,7 @@ cli.command("serve", "Serve design/.dist (set MARVER_PASSWORD to gate it)").opti
75
75
  serve(resolve(opts.root), port);
76
76
  });
77
77
  cli.command("comments <action> [value]", "Comment collaboration: connect <url> · sync · list · reply <thread> · resolve <thread> · invite <email> · revoke <email>").option("--root <dir>", "Host repo root", { default: "." }).option("--invite <token>", "connect: claim this invite instead of signing in").option("--canvas-password <password>", "connect: the canvas gate password (default $MARVER_PASSWORD or prompt)").option("--email <email>", "connect: account email (skips the prompt)").option("--password <password>", "connect: account password (skips the prompt - mind your shell history)").option("--name <name>", "connect --invite: display name for the new account").option("--open", "list: only unresolved threads").option("--json", "list: machine-readable output").option("--board <board>", "scope to one board").option("--body <text>", "reply: the reply text").option("--addressed-in <frame>", "resolve: the variant frame that answered the feedback").action(async (action, value, opts) => {
78
- const { commentsCommand } = await import("./comments-BrpC86Kf.mjs");
78
+ const { commentsCommand } = await import("./comments-J06jqCVV.mjs");
79
79
  try {
80
80
  await commentsCommand(resolve(opts.root), action, value, opts);
81
81
  } catch (err) {
@@ -83,6 +83,15 @@ cli.command("comments <action> [value]", "Comment collaboration: connect <url>
83
83
  process.exit(1);
84
84
  }
85
85
  });
86
+ cli.command("work <action> [...frames]", "Working state on the canvas: start <scene/frame ...> · done <scene/frame ...> | --all · list").option("--root <dir>", "Host repo root", { default: "." }).option("--ttl <minutes>", "start: minutes before the glow self-expires (default 10, max 30)").option("--all", "done: clear every working frame").action(async (action, frames, opts) => {
87
+ const { workCommand } = await import("./work-lzC-lPY0.mjs");
88
+ try {
89
+ await workCommand(resolve(opts.root), action, frames ?? [], opts);
90
+ } catch (err) {
91
+ console.error(`[${NAME}] ${err.message}`);
92
+ process.exit(1);
93
+ }
94
+ });
86
95
  cli.help();
87
96
  cli.version(version());
88
97
  cli.parse();
@@ -1,12 +1,12 @@
1
- import { appendEvents, listBoards, readLog } from "./comments-DSwMhdbZ.mjs";
2
- import { claimInvite, createInvite, inviteInfo, ownerName, publicUser, revokeUser, sessionUser, signIn, signOut, updateProfile } from "./auth-B36fMCM3.mjs";
1
+ import { appendEvents, listBoards, readLog } from "./comments-BZBKhKRO.mjs";
2
+ import { claimInvite, createInvite, inviteInfo, ownerName, publicUser, revokeUser, sessionUser, signIn, signOut, updateProfile } from "./auth-KQ9Aj-nB.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { randomBytes } from "node:crypto";
6
6
  //#region src/server/collab.ts
7
7
  const MONTH = 2592e3;
8
8
  const MAX_BODY = 262144;
9
- /** The permission seam (SPEC-M3 §3): v1 derives from publish policy + membership;
9
+ /** The permission seam: v1 derives from publish policy + membership;
10
10
  * a per-user ACL can replace the internals without touching a single route. */
11
11
  function can(user, rights, board, action) {
12
12
  if (action === "read") return board in rights;
@@ -3,7 +3,7 @@ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, re
3
3
  import { join } from "node:path";
4
4
  //#region src/server/comments.ts
5
5
  /**
6
- * The comment event store (SPEC-M3 §1) - an append-only JSONL log, one file per board.
6
+ * The comment event store - an append-only JSONL log, one file per board.
7
7
  *
8
8
  * Events are immutable and identified by client-generated UUIDs; two logs merge by SET
9
9
  * UNION on id. That single rule is the entire sync protocol: idempotent, order-
@@ -73,7 +73,7 @@ function listBoards(dir) {
73
73
  }
74
74
  /** Where serve keeps its live store: MARVER_DATA_DIR is REQUIRED when collaboration is
75
75
  * on - failing loudly beats silently writing into an ephemeral working directory that a
76
- * redeploy wipes (SPEC-M3 §2). */
76
+ * redeploy wipes. */
77
77
  function dataDir() {
78
78
  const dir = process.env.MARVER_DATA_DIR;
79
79
  if (!dir) throw new Error("collaboration needs a durable home: set MARVER_DATA_DIR to a directory on a persistent volume (comments written anywhere else would vanish on redeploy)");
@@ -1,14 +1,14 @@
1
1
  import { n as NAME } from "./cli.mjs";
2
2
  import { n as replay } from "./events-BMtBvvgU.mjs";
3
- import { appendEvents, listBoards, readLog } from "./comments-DSwMhdbZ.mjs";
4
- import { connect, connectClaim, loadCollab, syncOnce } from "./sync-Ch4Bymb1.mjs";
3
+ import { appendEvents, listBoards, readLog } from "./comments-BZBKhKRO.mjs";
4
+ import { connect, connectClaim, loadCollab, syncOnce } from "./sync-BJKKmy1n.mjs";
5
5
  import { n as localProfile } from "./profile-BkiWglVE.mjs";
6
6
  import { join } from "node:path";
7
7
  import { createInterface } from "node:readline";
8
8
  import { randomUUID } from "node:crypto";
9
9
  //#region src/cli/comments.ts
10
10
  /**
11
- * `marver comments <action>` (SPEC-M3 §2, §9) - the collaboration CLI.
11
+ * `marver comments <action>` - the collaboration CLI.
12
12
  *
13
13
  * connect <url> sign in (or --invite <token> to claim) against a published canvas;
14
14
  * stores the device credential in design/.local/collab.json