@marver-design/marver 0.22.0 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
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.22.1 - 2026-10-06
6
+
7
+ ### Changed
8
+
9
+ - **A project without `context/` hears about it.** The AGENTS contract opens with it: in a
10
+ repository with no `context/`, every reply in which an agent made, specced or changed a feature or
11
+ project board ends with one line - every feature board reads Backlog, nothing records what shipped,
12
+ want me to set it up? - until the human answers; a no stands for the session, and nothing is set up
13
+ unasked. `instructions/boards.md`, `npx marver boards`, `npx marver boards new` (feature, project),
14
+ `npx marver init` and `marver dev` at boot point at it. In 0.22.0 a routing row merely pointed at
15
+ the instruction: Claude Code and Codex, asked to spec a feature, both read it and never mentioned
16
+ it; with the line, both end their reply with it.
17
+ - `npx marver init` on an existing canvas no longer ends with the first-session hand-off - an
18
+ upgrade is not a first session.
19
+
20
+ ### Docs
21
+
22
+ - **"Set up our context" is the whole ask.** `instructions/context.md`'s setup is now the full recipe,
23
+ in order: the kind (`product` or `knowledge`), the draft from evidence, the canvas reading it -
24
+ typed folders, feature boards named after their capability, a start board - and the check in ci.
25
+ After upgrading (`npx marver init`), tell your agent: "Set up our context."
26
+
5
27
  ## 0.22.0 - 2026-10-06
6
28
 
7
29
  ### Added
@@ -1,4 +1,4 @@
1
- import { AREA, SURFACE, pool, shotConcurrency, withBrowser } from "./shot-C22Ues04.mjs";
1
+ import { AREA, SURFACE, pool, shotConcurrency, withBrowser } from "./shot-BAR8hmU9.mjs";
2
2
  import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, utimesSync, writeFileSync } from "node:fs";
3
3
  import { join, sep } from "node:path";
4
4
  import { createHash } from "node:crypto";
@@ -80,6 +80,7 @@ function boardsCommand(root, opts) {
80
80
  if (landing) console.log(`\nlanding board: ${landing}`);
81
81
  console.log(`registry: ${reg.state === "ok" ? "design/boards/_folders.json" : "none (no empty or ranked folders yet)"}`);
82
82
  if (skipped.length) console.log(`skipped (not regular files): ${skipped.join(", ")}`);
83
+ if ([...notes.values()].filter((n) => n.status).length && !existsSync(join(root, "context"))) console.log(`\nno context/ yet - every feature and project board reads Backlog. Agent: end your reply with the context line in design/AGENTS.md ("Before the method").`);
83
84
  }
84
85
  /** `marver boards new <name>` - a board in its type's starting layout (spec 20): a feature's three
85
86
  * phase scenes (spec, lo-fi, hi-fi) as three bands, a start board rendering context/INDEX.md and
@@ -1,8 +1,8 @@
1
1
  import { a as ROUTE, r as NAME } from "./cli.mjs";
2
2
  import { D as boardFields, H as isBoardName, L as buildTree, N as readRegistry, O as checkBoardsDir, R as flatten, Z as PROPOSED_PUBLISH, i as PUBLISHABLE, j as listBoardFiles, o as publishableStatus } from "./board-status-CWGHIdo_.mjs";
3
- import { r as scanFrames } from "./manifest-BqBcMJcd.mjs";
3
+ import { i as scanFrames } from "./manifest-CcdWx7ud.mjs";
4
4
  import { i as detectHost, n as loadConfig } from "./config-DJxMRVD8.mjs";
5
- import { n as tailwind3Css, r as tailwind4Plugin, t as marverPlugin } from "./plugin-8XntSCx_.mjs";
5
+ import { n as tailwind3Css, r as tailwind4Plugin, t as marverPlugin } from "./plugin-C5u07Yhv.mjs";
6
6
  import { cpSync, existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
7
7
  import { basename, dirname, join, sep } from "node:path";
8
8
  import { fileURLToPath } from "node:url";
@@ -754,7 +754,7 @@ async function buildSite(root, boardsFlag, allBoardsFlag, embedSeeds, textures =
754
754
  const realAssets = existsSync(assetsDir) ? realpathSync(assetsDir) : null;
755
755
  for (const r of refs) {
756
756
  if (!isLocalAssetRef(r) || !r.endsWith(".poster.png") || existsSync(join(assetsDir, r))) continue;
757
- const { ensurePoster } = await import("./poster-DRZDszTI.mjs");
757
+ const { ensurePoster } = await import("./poster-BduzQBYz.mjs");
758
758
  const g = await ensurePoster(assetsDir, r.slice(0, -11));
759
759
  if (!g.ok) throw new Error(`design/assets/${r}: ${g.error}`);
760
760
  console.log(` poster: rendered design/assets/${r} (${g.width}×${g.height})`);
@@ -821,7 +821,7 @@ async function buildSite(root, boardsFlag, allBoardsFlag, embedSeeds, textures =
821
821
  }));
822
822
  let textureLine = " textures: skipped (--no-textures)";
823
823
  if (textures && bakeGen) {
824
- const { bakePublished } = await import("./publish-bakes-CCWRV9iX.mjs");
824
+ const { bakePublished } = await import("./publish-bakes-b0jUwvM_.mjs");
825
825
  const pubFrameFile = new Map(pubFrames.map((f) => [f.id, f]));
826
826
  const r = await bakePublished({
827
827
  root,
package/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
3
3
  import { cac } from "cac";
4
- import { readFileSync, realpathSync } from "node:fs";
4
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
5
5
  import { dirname, join, resolve } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  //#region src/client/const.ts
@@ -82,7 +82,7 @@ cli.command("init", "Scaffold design/ in this repo").option("--mode <mode>", "st
82
82
  console.error(`[${NAME}] --kind ${opts.kind}: use product or knowledge`);
83
83
  process.exit(1);
84
84
  }
85
- const { init } = await import("./init-NJ3xjLVu.mjs");
85
+ const { init } = await import("./init-C_K-cjBQ.mjs");
86
86
  init(resolve$1(opts.root), {
87
87
  mode: opts.mode === "embedded" ? "embedded" : "studio",
88
88
  demo: opts.demo !== false,
@@ -90,7 +90,7 @@ cli.command("init", "Scaffold design/ in this repo").option("--mode <mode>", "st
90
90
  });
91
91
  });
92
92
  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) => {
93
- const { dev } = await import("./dev-BW7a5kDF.mjs");
93
+ const { dev } = await import("./dev-Cf4wLGe2.mjs");
94
94
  let port;
95
95
  if (opts.port !== void 0) {
96
96
  const n = Number(opts.port);
@@ -100,7 +100,7 @@ for (const [name, desc] of [["dev", "Start the local canvas (everything on: hot
100
100
  await dev(resolve$1(opts.root), port);
101
101
  });
102
102
  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("--embed-seeds", "Copy comment history INTO the web root (identifying - every event carries its author's email)").option("--no-textures", "Skip compiling the glass textures the published frames rest under (needs Chrome; a CI without one skips on its own)").option("--root <dir>", "Host repo root", { default: "." }).action(async (opts) => {
103
- const { buildSite } = await import("./build-Dd2Qm3OG.mjs");
103
+ const { buildSite } = await import("./build-B1kavcpc.mjs");
104
104
  try {
105
105
  const boards = opts.boards === void 0 ? void 0 : typeof opts.boards === "string" ? opts.boards : "";
106
106
  await buildSite(resolve$1(opts.root), boards, opts.allBoards === true, opts.embedSeeds === true, opts.textures !== false && !process.env.MARVER_NO_TEXTURES);
@@ -146,13 +146,14 @@ cli.command("work <action> [...frames]", "Working state on the canvas: start <sc
146
146
  }
147
147
  });
148
148
  cli.command("boards [action] [name]", "The sidebar as files: every folder and board in reading order, with types and statuses · new <name>: a board in its type's starting layout").option("--root <dir>", "Host repo root", { default: "." }).option("--json", "The tree as JSON ({ tree, boards, landing, registry })").option("--type <type>", "new: start | feature | surface | project | feedback | context | deck | archive (default: the folder's)").option("--folder <folder>", "new: the folder it sits in").option("--title <title>", "new: what humans see").option("--description <sentence>", "new: what agents read").option("--capability <slug>", "new: the capability it shows, when not its own name").action(async (action, name, opts) => {
149
- const { boardsCommand, boardsNew } = await import("./boards-BTGNPVMx.mjs");
149
+ const { boardsCommand, boardsNew } = await import("./boards-BwKI0qWd.mjs");
150
150
  try {
151
151
  if (action === void 0) boardsCommand(resolve$1(opts.root), opts);
152
152
  else if (action === "new") {
153
153
  if (!name) throw new Error("name the board: `boards new <name>`");
154
154
  const made = boardsNew(resolve$1(opts.root), name, opts);
155
155
  for (const c of made) console.log(` + ${c}`);
156
+ if (/\((feature|project)\b/.test(made[0] ?? "") && !existsSync(join(resolve$1(opts.root), "context"))) console.log(`\n note: no context/ yet - this board's status reads Backlog until there is one. Agent: end your reply with the context line in design/AGENTS.md ("Before the method").`);
156
157
  if (made.some((c) => c.endsWith("/_brief.md")) && !made.some((c) => /\.(tsx|jsx|html)$/.test(c))) console.log(`\n next: a frame you make in these scenes shows on the board once it is pinned - a node in\n design/boards/${name}.json (an agent does this as it works: design/AGENTS.md)`);
157
158
  } else throw new Error(`unknown action "${action}" - \`boards\` lists, \`boards new <name>\` creates`);
158
159
  } catch (err) {
@@ -161,7 +162,7 @@ cli.command("boards [action] [name]", "The sidebar as files: every folder and bo
161
162
  }
162
163
  });
163
164
  cli.command("folders <action> [...modules]", "The shared sidebar's typed folders · add <start|features|surfaces|projects|feedback|context|decks|archive ...>").option("--root <dir>", "Host repo root", { default: "." }).action(async (action, modules, opts) => {
164
- const { foldersAdd } = await import("./boards-BTGNPVMx.mjs");
165
+ const { foldersAdd } = await import("./boards-BwKI0qWd.mjs");
165
166
  try {
166
167
  if (action !== "add") throw new Error(`unknown action "${action}" - \`folders add <module ...>\``);
167
168
  const { added, existing } = foldersAdd(resolve$1(opts.root), modules);
@@ -173,7 +174,7 @@ cli.command("folders <action> [...modules]", "The shared sidebar's typed folders
173
174
  }
174
175
  });
175
176
  cli.command("context <action>", "The project's context/ (spec 19) · init: create it · check: keep it true (exit 0 pass, 1 fail, 2 cannot determine) · index: regenerate the index's capability table").option("--root <dir>", "Host repo root", { default: "." }).option("--base <ref>", "check: the pull request's base - a change to a contracted capability's code must change its contract or say why (in ci, read from the event)").option("--head <ref>", "check: the pull request's head (default HEAD)").option("--body-file <file>", "check: the pull request's body, for `no-contract-change: <capability> - <why>`").option("--json", "check: the findings as JSON ({ exit, failures, unsure, notes })").option("--kind <kind>", "init: product (a shipped record) or knowledge (a delivered record)", { default: "product" }).action(async (action, opts) => {
176
- const ctx = await import("./context-DUAENlJ6.mjs");
177
+ const ctx = await import("./context-D4t59wDi.mjs");
177
178
  const root = resolve$1(opts.root);
178
179
  try {
179
180
  if (action === "init") {
@@ -2,7 +2,7 @@ import { r as NAME } from "./cli.mjs";
2
2
  import { C as tables, S as proseLines, _ as isRecordTable, b as looseTables, c as CITED_FILE, d as LEVEL, f as availableLevels, g as hasGlob, h as globRe, k as checkRealDirs, l as EVIDENCE_COLUMN, m as frontMatter, p as capabilityTable, s as CITATION, tt as readStatusWord, u as GENERATED, v as levelsIn, w as wordCount, x as parseMap, y as lf } from "./board-status-CWGHIdo_.mjs";
3
3
  import { pkgDir } from "./managed-HwHVNI3h.mjs";
4
4
  import { t as writeManaged } from "./managed-write-Bo-oPc-i.mjs";
5
- import { r as scanFrames } from "./manifest-BqBcMJcd.mjs";
5
+ import { i as scanFrames } from "./manifest-CcdWx7ud.mjs";
6
6
  import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
7
7
  import { basename, dirname, join, normalize, relative, sep } from "node:path";
8
8
  import { execFileSync } from "node:child_process";
@@ -1,7 +1,7 @@
1
1
  import { n as replay } from "./events-B3LBn74P.mjs";
2
2
  import { i as readLog, r as listBoards, t as appendEvents } from "./comments-DZyobpxG.mjs";
3
3
  import { n as localProfile } from "./profile-BjAPAJSb.mjs";
4
- import { a as toFrameId } from "./manifest-BqBcMJcd.mjs";
4
+ import { o as toFrameId } from "./manifest-CcdWx7ud.mjs";
5
5
  import { workActivity } from "./work-CLrmY-vQ.mjs";
6
6
  import { r as deviceId, t as has } from "./ledger-Bu0BjqIe.mjs";
7
7
  import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, watch, writeFileSync, writeSync } from "node:fs";
@@ -1,7 +1,7 @@
1
1
  import { i as PKG, r as NAME } from "./cli.mjs";
2
2
  import { i as detectHost, n as loadConfig } from "./config-DJxMRVD8.mjs";
3
- import { n as tailwind3Css, r as tailwind4Plugin, t as marverPlugin } from "./plugin-8XntSCx_.mjs";
4
- import { readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
3
+ import { n as tailwind3Css, r as tailwind4Plugin, t as marverPlugin } from "./plugin-C5u07Yhv.mjs";
4
+ import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
5
5
  import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { createLogger, createServer, parseAst, searchForWorkspaceRoot } from "vite";
@@ -454,12 +454,12 @@ async function dev(root, portFlag) {
454
454
  console.log(`\n ${NAME} · ${projectName} → http://localhost:${port}/\n`);
455
455
  setTimeout(async () => {
456
456
  try {
457
- await (await import("./shot-C22Ues04.mjs")).sweepGhosts((m) => console.log(` ${m}`));
457
+ await (await import("./shot-BAR8hmU9.mjs")).sweepGhosts((m) => console.log(` ${m}`));
458
458
  } catch {}
459
459
  }, 0);
460
460
  try {
461
461
  const { staleManagedInstructions } = await import("./managed-HwHVNI3h.mjs");
462
- const { installedVersion } = await import("./plugin-8XntSCx_.mjs").then((n) => n.i);
462
+ const { installedVersion } = await import("./plugin-C5u07Yhv.mjs").then((n) => n.i);
463
463
  const stale = staleManagedInstructions(root);
464
464
  if (stale.length) {
465
465
  const shown = stale.slice(0, 4).join(", ") + (stale.length > 4 ? `, +${stale.length - 4} more` : "");
@@ -467,6 +467,12 @@ async function dev(root, portFlag) {
467
467
  console.log(` instructions predate ${v ? NAME + " " + v : "the installed version"} (${shown}) - run: npx ${NAME} init\n`);
468
468
  }
469
469
  } catch {}
470
+ try {
471
+ if (!existsSync(join(root, "context"))) {
472
+ const n = (await import("./manifest-CcdWx7ud.mjs").then((n) => n.r)).statusBoards(root).length;
473
+ if (n) console.log(` ${n} feature/project board${n === 1 ? "" : "s"}, no context/ - every status reads Backlog. To start it, tell your agent: "Set up our context."\n`);
474
+ }
475
+ } catch {}
470
476
  const { loadCollab, syncOnce } = await import("./sync-BZaCWqK-.mjs").then((n) => n.o);
471
477
  if (loadCollab(root)) console.log(` comments: syncing with the published canvas\n`);
472
478
  let syncing = false;
@@ -497,7 +503,7 @@ async function dev(root, portFlag) {
497
503
  });
498
504
  }
499
505
  if (config.jam) {
500
- const { startJam } = await import("./daemon-BmkwErpC.mjs");
506
+ const { startJam } = await import("./daemon-PmNqoOAk.mjs");
501
507
  const jam = startJam(root, config.jam, (m) => console.log(m), (board) => server.ws.send("sh:jam-comment", { board }));
502
508
  if (jam) {
503
509
  const close = server.close.bind(server);
@@ -2,7 +2,7 @@ import { r as NAME } from "./cli.mjs";
2
2
  import { E as addFolders, J as FOLDER_MODULES, X as KIND_FOLDERS } from "./board-status-CWGHIdo_.mjs";
3
3
  import { MANAGED_PREFIX, enumerateInstructionTemplates, hashBody, pkgDir } from "./managed-HwHVNI3h.mjs";
4
4
  import { t as writeManaged } from "./managed-write-Bo-oPc-i.mjs";
5
- import { o as writeManifest, r as scanFrames } from "./manifest-BqBcMJcd.mjs";
5
+ import { i as scanFrames, s as writeManifest } from "./manifest-CcdWx7ud.mjs";
6
6
  import { a as readJson, i as detectHost, r as detectAgent, t as DEFAULTS } from "./config-DJxMRVD8.mjs";
7
7
  import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
8
8
  import { basename, dirname, join, relative } from "node:path";
@@ -234,10 +234,11 @@ function init(root, opts) {
234
234
  console.log(`\n commit design/ - only .local/ is ignored`);
235
235
  console.log(` uninstall = delete design/, remove the ${NAME} dependency${host.tsconfigSweepsDesign ? ", revert the \"design\" line in tsconfig exclude" : ""}`);
236
236
  if (!noApp(host) && !existsSync(join(design, "DESIGN.md"))) console.log(`\n note: design/DESIGN.md (the brand doc) does not exist yet - have your agent create it from the app's tokens (instructions/brand.md, Path A) to reach the idle state.`);
237
+ if (!existsSync(join(root, "context"))) console.log(`\n note: no context/ yet - feature and project boards read Backlog until there is one, and nothing records what shipped. To start it, tell your agent: "Set up our context." (design/instructions/context.md)`);
237
238
  if (!jamAgent) console.log(`\n note: Live Jam found no agent CLI on PATH (claude, codex, cursor, droid, opencode, grok, or pi) - install one and it arms itself on the next \`${NAME} dev\`.`);
238
239
  else if (created.includes("design/config.ts")) console.log(`\n Live Jam is on (${jamAgent}): tag @${NAME} in a canvas comment and your agent does the work, then replies in the thread.`);
239
240
  console.log(`\n next: npx ${NAME} dev (or: npx ${NAME} canvas - same thing; canvas on http://localhost:${DEFAULTS.port} by default)\n`);
240
- if (!noApp(host)) console.log(` then, to your agent: "Read design/AGENTS.md. This is our first session - follow design/instructions/welcome.md."\n`);
241
+ if (!noApp(host) && created.includes("design/config.ts")) console.log(` then, to your agent: "Read design/AGENTS.md. This is our first session - follow design/instructions/welcome.md."\n`);
241
242
  }
242
243
  /** The host's path aliases, re-rooted one level down for design/tsconfig.json.
243
244
  * "./src/*" becomes "../src/*" so `@/` imports resolve from inside design/. */
@@ -1,9 +1,29 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
1
2
  import { i as PKG, n as CONTENT_WIDTH } from "./cli.mjs";
2
3
  import { D as boardFields, H as isBoardName, L as buildTree, N as readRegistry, O as checkBoardsDir, R as flatten, U as readDescription, W as readTitle, at as hash, j as listBoardFiles, k as checkRealDirs, t as annotateBoards, z as folderEntries } from "./board-status-CWGHIdo_.mjs";
3
4
  import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
4
5
  import { join, relative, sep } from "node:path";
5
6
  import { randomBytes } from "node:crypto";
6
7
  //#region src/server/manifest.ts
8
+ var manifest_exports = /* @__PURE__ */ __exportAll({
9
+ NOTE_MAX: () => NOTE_MAX,
10
+ affectedFrameIds: () => affectedFrameIds,
11
+ codeOnly: () => codeOnly,
12
+ contentScan: () => contentScan,
13
+ contentWidthOf: () => contentWidthOf,
14
+ extractMeta: () => extractMeta,
15
+ frontMatter: () => frontMatter,
16
+ isNoteFile: () => isNoteFile,
17
+ noteFileFor: () => noteFileFor,
18
+ readNote: () => readNote,
19
+ scanFrames: () => scanFrames,
20
+ sceneBrief: () => sceneBrief,
21
+ sceneNote: () => sceneNote,
22
+ setSceneTitle: () => setSceneTitle,
23
+ statusBoards: () => statusBoards,
24
+ toFrameId: () => toFrameId,
25
+ writeManifest: () => writeManifest
26
+ });
7
27
  const FRAME_EXT = /\.(tsx|jsx|html)$/;
8
28
  const RESERVED_SCENES = /* @__PURE__ */ new Set(["components", "screens"]);
9
29
  /** Sticky notes (spec 18): the longest note the manifest carries - a note is an aside, not a spec. */
@@ -296,6 +316,9 @@ function scanBoards(root) {
296
316
  ...boards.length ? { boards } : {}
297
317
  };
298
318
  }
319
+ /** The boards that wear a status (feature and project, by their resolved type) - for the dev
320
+ * server's word at boot when there is no context/ to read it from. */
321
+ const statusBoards = (root) => (scanBoards(root).boards ?? []).filter((b) => b.type === "feature" || b.type === "project").map((b) => b.name);
299
322
  function scanFrames(root, project) {
300
323
  const design = join(root, "design");
301
324
  const frames = [];
@@ -452,4 +475,4 @@ function writeManifest(root, manifest) {
452
475
  return true;
453
476
  }
454
477
  //#endregion
455
- export { toFrameId as a, setSceneTitle as i, isNoteFile as n, writeManifest as o, scanFrames as r, affectedFrameIds as t };
478
+ export { setSceneTitle as a, scanFrames as i, isNoteFile as n, toFrameId as o, manifest_exports as r, writeManifest as s, affectedFrameIds as t };
@@ -2,7 +2,7 @@ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { a as ROUTE, i as PKG, r as NAME } from "./cli.mjs";
3
3
  import { A as isRegularFile, B as folderMap, D as boardFields, F as BOARD_NAME, G as validateWire, I as FOLDERS_FILE, K as wireKids, L as buildTree, M as nodeExists, N as readRegistry, O as checkBoardsDir, P as withRegistryLock, T as AUTHOR_FIELDS, U as readDescription, W as readTitle, Y as HAS_STATUS, at as hash, et as readReason, it as settableStatuses, j as listBoardFiles, n as readContextFacts, nt as readType, rt as resolveType, t as annotateBoards, tt as readStatusWord } from "./board-status-CWGHIdo_.mjs";
4
4
  import { n as localProfile, t as isConnected } from "./profile-BjAPAJSb.mjs";
5
- import { i as setSceneTitle, n as isNoteFile, o as writeManifest, r as scanFrames, t as affectedFrameIds } from "./manifest-BqBcMJcd.mjs";
5
+ import { a as setSceneTitle, i as scanFrames, n as isNoteFile, s as writeManifest, t as affectedFrameIds } from "./manifest-CcdWx7ud.mjs";
6
6
  import { n as loadConfig } from "./config-DJxMRVD8.mjs";
7
7
  import { copyFileSync, existsSync, linkSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, watch, writeFileSync } from "node:fs";
8
8
  import { basename, dirname, join, resolve, sep } from "node:path";
@@ -546,7 +546,7 @@ function apiMiddleware(root, opts = {}) {
546
546
  };
547
547
  const asPng = url.searchParams.get("format") === "png";
548
548
  const origin = opts.origin?.() ?? `http://${req.headers.host ?? "localhost"}`;
549
- const { shootFrame } = await import("./shot-C22Ues04.mjs");
549
+ const { shootFrame } = await import("./shot-BAR8hmU9.mjs");
550
550
  const r = await shootFrame({
551
551
  root,
552
552
  viewports: opts.viewports ?? {},
@@ -596,7 +596,7 @@ function apiMiddleware(root, opts = {}) {
596
596
  if (typeof theme !== "string" || !/^[a-z0-9-]+$/i.test(theme)) return json(res, 400, { error: "invalid theme" });
597
597
  const scale = body.scale === void 0 ? void 0 : body.scale;
598
598
  if (scale !== void 0 && !(Number.isInteger(scale) && scale >= 1 && scale <= 4)) return json(res, 400, { error: "invalid scale" });
599
- const { resolveFrames, shootBatch } = await import("./shot-C22Ues04.mjs");
599
+ const { resolveFrames, shootBatch } = await import("./shot-BAR8hmU9.mjs");
600
600
  const sel = resolveFrames(root, body);
601
601
  if (!sel.ok) return json(res, sel.status, { error: sel.error });
602
602
  const origin = opts.origin?.() ?? `http://${req.headers.host ?? "localhost"}`;
@@ -624,7 +624,7 @@ function apiMiddleware(root, opts = {}) {
624
624
  return json(res, 400, { error: "malformed JSON" });
625
625
  }
626
626
  if (!body || typeof body !== "object" || !Array.isArray(body.asks) || !body.asks.length || body.asks.length > 200) return json(res, 400, { error: "asks must list 1-200 frames" });
627
- const { bakeBatch, ASK_MAX } = await import("./bake-BSX4XR_U.mjs");
627
+ const { bakeBatch, ASK_MAX } = await import("./bake-CvY8QR3L.mjs");
628
628
  let manifest = {};
629
629
  try {
630
630
  manifest = JSON.parse(readFileSync(join(root, "design", "manifest.json"), "utf8"));
@@ -668,8 +668,8 @@ function apiMiddleware(root, opts = {}) {
668
668
  if (path === "poster" && req.method === "GET") {
669
669
  if (!ownerGated(req)) return json(res, 403, { error: "forbidden" });
670
670
  const src = url.searchParams.get("src") ?? "";
671
- const { ensurePoster, isLocalClip } = await import("./poster-DRZDszTI.mjs");
672
- const { isLocalAssetRef } = await import("./build-Dd2Qm3OG.mjs");
671
+ const { ensurePoster, isLocalClip } = await import("./poster-BduzQBYz.mjs");
672
+ const { isLocalAssetRef } = await import("./build-B1kavcpc.mjs");
673
673
  if (!isLocalAssetRef(src) || !isLocalClip(src)) return json(res, 400, { error: "src must be a clip under design/assets/" });
674
674
  const r = await ensurePoster(join(root, "design", "assets"), src);
675
675
  if (!r.ok) return json(res, r.error.includes("does not exist") ? 404 : 503, { error: r.error });
@@ -1151,7 +1151,7 @@ function marverPlugin(ctx) {
1151
1151
  };
1152
1152
  const prune = () => setTimeout(async () => {
1153
1153
  try {
1154
- (await import("./bake-BSX4XR_U.mjs")).pruneBakes(root, bakeGen);
1154
+ (await import("./bake-CvY8QR3L.mjs")).pruneBakes(root, bakeGen);
1155
1155
  } catch {}
1156
1156
  }, 500);
1157
1157
  prune();
@@ -1303,7 +1303,7 @@ function marverPlugin(ctx) {
1303
1303
  });
1304
1304
  return;
1305
1305
  }
1306
- const { shootFrame, shootBatch, resolveFrames } = await import("./shot-C22Ues04.mjs");
1306
+ const { shootFrame, shootBatch, resolveFrames } = await import("./shot-BAR8hmU9.mjs");
1307
1307
  if (ways[0] === "frame") {
1308
1308
  if (typeof spec.frame !== "string") {
1309
1309
  write({
@@ -1,4 +1,4 @@
1
- import { capture, t as findChrome } from "./shot-C22Ues04.mjs";
1
+ import { capture, t as findChrome } from "./shot-BAR8hmU9.mjs";
2
2
  import { constants, copyFileSync, existsSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { basename, dirname, join, sep } from "node:path";
4
4
  import { pathToFileURL } from "node:url";
@@ -1,6 +1,6 @@
1
1
  import { o as slideSize } from "./cli.mjs";
2
- import { planShot, t as findChrome } from "./shot-C22Ues04.mjs";
3
- import { ASK_MAX, bakeBatch } from "./bake-BSX4XR_U.mjs";
2
+ import { planShot, t as findChrome } from "./shot-BAR8hmU9.mjs";
3
+ import { ASK_MAX, bakeBatch } from "./bake-CvY8QR3L.mjs";
4
4
  import { MIME } from "./serve-z5qtj_wJ.mjs";
5
5
  import { copyFileSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
6
6
  import { extname, isAbsolute, join, relative, resolve } from "node:path";
@@ -337,8 +337,8 @@ async function shootFrame(opts) {
337
337
  };
338
338
  const plan = planShot(frame, viewports, size);
339
339
  if (frame.kind !== "html") try {
340
- const { scanAssetRefs } = await import("./build-Dd2Qm3OG.mjs");
341
- const { ensurePoster } = await import("./poster-DRZDszTI.mjs");
340
+ const { scanAssetRefs } = await import("./build-B1kavcpc.mjs");
341
+ const { ensurePoster } = await import("./poster-BduzQBYz.mjs");
342
342
  const refs = scanAssetRefs(readFileSync(join(root, frame.file), "utf8"), frame.file);
343
343
  for (const r of refs) if (r.endsWith(".poster.png")) await ensurePoster(join(root, "design", "assets"), r.slice(0, -11));
344
344
  } catch {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marver-design/marver",
3
- "version": "0.22.0",
3
+ "version": "0.22.1",
4
4
  "description": "The agent-native design canvas. A design/ folder, one command, a canvas of live frames built from your repo's real components - comment @marver and your own coding agent does the work. The tool ships no AI.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -5,6 +5,17 @@ Never drive or automate the canvas UI; read and write files only. (Starting
5
5
  `npx marver dev` so the human has a live canvas - first session, or on request -
6
6
  is the one allowed touch.)
7
7
 
8
+ ## Before the method: a project with no `context/`
9
+
10
+ This repository has no `context/` directory? Then **every reply in which you made, specced or changed
11
+ a feature or project board ends with this line**, word for word, until the human answers it:
12
+
13
+ > This project has no `context/` yet, so every feature board reads Backlog and nothing records what
14
+ > shipped. Want me to set it up? A few questions, then a draft from the code and the deploy history.
15
+
16
+ A yes: instructions/context.md, "Setting up". A no stands for the session - stop asking. Never set
17
+ it up unasked.
18
+
8
19
  ## The method (binding)
9
20
 
10
21
  Design work moves through phases. BEFORE working in a phase, read its instruction
@@ -5,6 +5,17 @@ Never drive or automate the canvas UI; read and write files only. (Starting
5
5
  `npx marver dev` so the human has a live canvas - first session, or on request -
6
6
  is the one allowed touch.)
7
7
 
8
+ ## Before the method: a project with no `context/`
9
+
10
+ This repository has no `context/` directory? Then **every reply in which you made, specced or changed
11
+ a feature or project board ends with this line**, word for word, until the human answers it:
12
+
13
+ > This project has no `context/` yet, so every feature board reads Backlog and nothing records what
14
+ > shipped. Want me to set it up? A few questions, then a draft from the code and the deploy history.
15
+
16
+ A yes: instructions/context.md, "Setting up". A no stands for the session - stop asking. Never set
17
+ it up unasked.
18
+
8
19
  ## The method (binding)
9
20
 
10
21
  Design work moves through phases. BEFORE working in a phase, read its instruction
@@ -176,7 +176,9 @@ boards fall back to the parent folder's type). Moving a board changes an inherit
176
176
  capability is the board's name, or `"capability": "<slug>"`. You may decide only three things on
177
177
  the board: `"status": "archived"`, `"paused"`, or `"blocked"` with `"reason": "<one sentence>"`.
178
178
  **Never write `"status": "done"`** - Done comes from `context/shipped.md`, and the check fails it.
179
- Without `context/` a board may also say `"todo"`, `"backlog"` or `"in-progress"` by hand.
179
+ **No `context/`? Your reply ends with the context line** from design/AGENTS.md ("Before the method")
180
+ - every feature board reads Backlog until there is one. Only when the human declines may a board
181
+ say `"todo"`, `"backlog"` or `"in-progress"` by hand.
180
182
  A person sets the same from the sidebar (right-click, Change status…) - it rewrites only `status`
181
183
  and `reason`, so re-read a board before you edit it.
182
184
  - In progress fills by phase: the phase scenes above, or `phase: spec | lofi | hifi` in a scene's
@@ -98,11 +98,20 @@ Context starts with people. Ask, in one message:
98
98
  3. What is private, and who may see what?
99
99
  4. How is this project named in shared spaces - keywords, tags?
100
100
 
101
- Then `npx marver context init`, record the answers in `context/people.md`, and draft from evidence:
102
- the shipped record from deploy runs and the changelog (`unknown` where nothing proves it), a map from
103
- the code, contracts for the capabilities that matter now. **Context that exists but is scattered**
104
- - specs out of date, nobody sure what is live - is a job for
105
- `context/playbooks/reorganize-context/PLAYBOOK.md`, not for a rewrite.
101
+ Then, in order - this is the whole recipe; "set up our context" asks for all of it:
102
+ 1. `npx marver context init --kind product` - or `--kind knowledge` when the work is deliverables
103
+ for clients rather than a product people use. Record the answers in `context/people.md`.
104
+ 2. Draft from evidence: the shipped record from deploy runs and the changelog (`unknown` where
105
+ nothing proves it), a map from the code, contracts for the capabilities that matter now.
106
+ 3. Let the canvas read it: `npx marver init --kind <the same kind>` adds the typed folders (it never
107
+ moves a board); put each feature board in Features (a project in Projects), named after its
108
+ capability or saying `"capability": "<slug>"`; then `npx marver boards new home --type start
109
+ --folder start-here`.
110
+ 4. Put the check in ci - a step running `npx marver context check` on a full-history checkout
111
+ (`fetch-depth: 0`) - and make it pass before you hand over.
112
+
113
+ **Context that exists but is scattered** - specs out of date, nobody sure what is live - is a job
114
+ for `context/playbooks/reorganize-context/PLAYBOOK.md`, not for a rewrite.
106
115
 
107
116
  ## The check
108
117