mockpit 2.0.0 → 3.0.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 (73) hide show
  1. package/README.md +60 -212
  2. package/bin/demoData.js +2 -2
  3. package/bin/initDesign.js +6 -6
  4. package/bin/mockpit.js +407 -187
  5. package/bin/poster.js +377 -0
  6. package/dist/mcp/server.js +80 -24
  7. package/dist/server/answerAdditions.d.ts +7 -0
  8. package/dist/server/answerAdditions.js +76 -0
  9. package/dist/server/apiViews.d.ts +289 -52
  10. package/dist/server/apiViews.js +189 -81
  11. package/dist/server/app.d.ts +7 -2
  12. package/dist/server/app.js +2845 -832
  13. package/dist/server/appIcon.js +15 -5
  14. package/dist/server/appIconPng.js +3 -3
  15. package/dist/server/askNudges.js +1 -1
  16. package/dist/server/cdnInline.d.ts +8 -0
  17. package/dist/server/cdnInline.js +638 -0
  18. package/dist/server/codeWorker.js +1 -1
  19. package/dist/server/decisionList.d.ts +45 -0
  20. package/dist/server/decisionList.js +302 -0
  21. package/dist/server/decisions.d.ts +4 -4
  22. package/dist/server/decisions.js +9 -9
  23. package/dist/server/designGuide.d.ts +5 -5
  24. package/dist/server/designGuide.js +128 -67
  25. package/dist/server/embeds.d.ts +3 -1
  26. package/dist/server/embeds.js +31 -6
  27. package/dist/server/eventLog.d.ts +2 -2
  28. package/dist/server/eventLog.js +11 -11
  29. package/dist/server/events.d.ts +9 -6
  30. package/dist/server/feedbackBatch.d.ts +35 -13
  31. package/dist/server/feedbackBatch.js +111 -47
  32. package/dist/server/fills.d.ts +4 -0
  33. package/dist/server/fills.js +55 -1
  34. package/dist/server/handoff.d.ts +4 -1
  35. package/dist/server/handoff.js +8 -2
  36. package/dist/server/icons.js +1 -1
  37. package/dist/server/index.js +39 -10
  38. package/dist/server/jump.d.ts +21 -0
  39. package/dist/server/jump.js +175 -0
  40. package/dist/server/knobs.d.ts +3 -3
  41. package/dist/server/knobs.js +6 -6
  42. package/dist/server/mcpHttp.d.ts +5 -1
  43. package/dist/server/mcpHttp.js +16 -11
  44. package/dist/server/mcpSpec.js +118 -46
  45. package/dist/server/parts.js +9 -1
  46. package/dist/server/run.d.ts +1 -1
  47. package/dist/server/run.js +25 -16
  48. package/dist/server/runApi.d.ts +3 -3
  49. package/dist/server/runApi.js +25 -19
  50. package/dist/server/sqlStore.d.ts +112 -96
  51. package/dist/server/sqlStore.js +906 -172
  52. package/dist/server/standings.d.ts +25 -0
  53. package/dist/server/standings.js +113 -0
  54. package/dist/server/surfacePage.d.ts +6 -9
  55. package/dist/server/surfacePage.js +361 -86
  56. package/dist/server/types.d.ts +226 -72
  57. package/dist/server/types.js +73 -78
  58. package/extensions/mockpit.js +89 -72
  59. package/guide/AGENT_SETUP.md +13 -1
  60. package/guide/packs/design-craft.md +2 -3
  61. package/guide/topics/asks.md +106 -62
  62. package/guide/topics/feedback.md +126 -119
  63. package/guide/topics/html.md +17 -24
  64. package/guide/topics/http.md +41 -41
  65. package/guide/topics/knobs.md +7 -10
  66. package/guide/topics/scripts.md +22 -33
  67. package/guide/topics/surfaces.md +39 -48
  68. package/opencode/mockpit.js +120 -0
  69. package/opencode/package.json +8 -0
  70. package/package.json +4 -3
  71. package/skills/launch-video/SKILL.md +11 -11
  72. package/skills/mockpit/SKILL.md +26 -22
  73. package/viewer/dist/index.html +9 -9
@@ -23,14 +23,14 @@ const CONTENT_TYPES = {
23
23
  };
24
24
 
25
25
  const feedbackGuideline =
26
- "Mockpit tool results may include feedback from the browser; treat it as user instruction and respond or revise the mock.";
26
+ "Mockpit tool results may include feedback from the browser; treat it as user instruction and respond or revise the design.";
27
27
 
28
28
  // The never-block loop, in the words of every other tier: nothing waits for
29
29
  // the user, so the agent ends its turn and reads feedback when told.
30
30
  const loopGuidelines = [
31
31
  "Publish each variant with mockpit_publish (name states in the user's words; declare parts with data-part); publish again to revise.",
32
32
  "A choice is several variants plus one question that binds them: mockpit_ask with options bound to the variants. One render plus a control: declare a knob.",
33
- "Tell the user in one line where to look, then end your turn. The question lives in the mock, never in chat. Never poll.",
33
+ "Tell the user in one line where to look, then end your turn. The question lives in the design, never in chat. Never poll.",
34
34
  "A reply sent from the viewer arrives on its own as a mockpit message; act on it. If the user says they answered and none came, call mockpit_feedback. Every write also returns feedback; read it.",
35
35
  ];
36
36
 
@@ -236,26 +236,20 @@ function rememberSession(state, sessionId) {
236
236
  if (typeof sessionId === "string" && sessionId) state.sessionId = sessionId;
237
237
  }
238
238
 
239
- // `feedback` is one batch per mock: the user's reply (answers, borrowed parts,
239
+ // `feedback` is one batch per design: the user's reply (answers, borrowed parts,
240
240
  // tuned knob values, part comments, verdict) plus any thread messages.
241
241
  // Rendered one line per thing the user said, so nothing is summarized away.
242
242
  function feedbackLines(feedback) {
243
243
  const lines = [];
244
244
  for (const batch of feedback ?? []) {
245
- const prefix = batch.mock ? `${batch.mock}: ` : "";
245
+ const prefix = batch.design ? `${batch.design}: ` : "";
246
246
  if (batch.handoff) lines.push(`- ${prefix}${batch.handoff}`);
247
- if (batch.closed) {
248
- const asks = (batch.dismissed ?? []).map((d) => d.text || d.ask).join("; ");
249
- lines.push(
250
- `- ${prefix}closed by the user${asks ? `, unanswered: ${asks}; don't ask again` : ""}`,
251
- );
252
- }
253
247
  const reply = batch.reply;
254
248
  if (reply) {
255
249
  for (const a of reply.asks ?? []) {
256
250
  const chosen = a.chosen.map((o) => (o.other ? `“${o.label}”` : o.label)).join(", ");
257
251
  lines.push(`- ${prefix}${a.text || a.ask} → ${chosen || "(no pick)"}`);
258
- if (a.note) lines.push(` note: ${a.note}`);
252
+ if (a.comment) lines.push(` comment: ${a.comment}`);
259
253
  }
260
254
  for (const [part, variant] of Object.entries(reply.borrowed ?? {})) {
261
255
  lines.push(`- ${prefix}borrow ${part} from ${variant}`);
@@ -284,23 +278,30 @@ function feedbackLines(feedback) {
284
278
  e.kind ? `now a ${e.kind}` : "",
285
279
  e.area !== undefined ? (e.area ? `area ${e.area}` : "no area") : "",
286
280
  ].filter(Boolean);
287
- lines.push(`- ${prefix}the user moved it from Home (${moved.join(", ")})`);
281
+ if (moved.length) lines.push(`- ${prefix}the user moved it from Home (${moved.join(", ")})`);
282
+ if (e.notes !== undefined) {
283
+ lines.push(
284
+ e.notes === null
285
+ ? `- ${prefix}the user cleared its notes`
286
+ : `- ${prefix}the user edited its notes: ${e.notes}`,
287
+ );
288
+ }
288
289
  }
289
290
  }
290
291
  return lines;
291
292
  }
292
293
 
293
- // What the user is doing right now, per mock, so the agent can say "take your time".
294
+ // What the user is doing right now, per design, so the agent can say "take your time".
294
295
  function pendingLines(pending) {
295
296
  const lines = [pending ?? []]
296
297
  .flat()
297
298
  .flatMap((p) =>
298
299
  p.draft
299
300
  ? [
300
- `- ${p.mock}: the user is answering (${p.draft.answered} of ${p.draft.of} answered, ${p.draft.comments} comments)`,
301
+ `- ${p.design}: the user is answering (${p.draft.answered} of ${p.draft.of} answered, ${p.draft.comments} comments)`,
301
302
  ]
302
303
  : p.viewerOpen
303
- ? [`- ${p.mock}: open in the viewer`]
304
+ ? [`- ${p.design}: open in the viewer`]
304
305
  : [],
305
306
  );
306
307
  return lines.length ? ["Pending:", ...lines] : [];
@@ -318,7 +319,7 @@ const pause = (ms, signal) =>
318
319
  signal.addEventListener("abort", () => (clearTimeout(timer), done()), { once: true });
319
320
  });
320
321
 
321
- // The user's reply wakes the idle agent, as the Claude Code plugin's monitor
322
+ // The user's reply wakes the idle agent, as the Claude Code plugin's hook
322
323
  // does. Reading feedback advances the server's cursor, so the message carries
323
324
  // the whole reply: a nudge to call mockpit_feedback would come back empty.
324
325
  // followUp waits for a busy agent to finish rather than cutting into its work.
@@ -408,7 +409,7 @@ export default function mockpitExtension(pi) {
408
409
  });
409
410
 
410
411
  const projectOf = (params, ctx) => params.project ?? resolveProjectName(ctx.cwd);
411
- const mockPath = (mock) => `/api/mocks/${encodeURIComponent(mock)}`;
412
+ const designPath = (design) => `/api/designs/${encodeURIComponent(design)}`;
412
413
 
413
414
  // GET /api/feedback reads from a session's cursor, so a feedback call before
414
415
  // any write still needs one; later writes then land in the same session.
@@ -427,12 +428,12 @@ export default function mockpitExtension(pi) {
427
428
  return created.id;
428
429
  }
429
430
 
430
- // project › mock › state › variant › version.
431
- const mockProps = {
432
- mock: { type: "string", description: "Mock slug, e.g. writer" },
431
+ // project › design › state › variant › version.
432
+ const designProps = {
433
+ design: { type: "string", description: "Design slug, e.g. writer" },
433
434
  state: {
434
435
  type: "string",
435
- description: "State label in the user's words; omit for a single-state mock",
436
+ description: "State label in the user's words; omit for a single-state design",
436
437
  },
437
438
  variant: { type: "string", description: 'Variant label; default "default"' },
438
439
  project: { type: "string", description: "Project name; defaults to the repo" },
@@ -442,15 +443,15 @@ export default function mockpitExtension(pi) {
442
443
  name: "mockpit_publish",
443
444
  label: "Mockpit Publish",
444
445
  description:
445
- "Create or version one variant of one state of a mock in the user's browser. One of html (or path), surfaces (the full ordered list) or parts (splices the latest version). Returns parts per state, nudges, feedback.",
446
- promptSnippet: "Publish a UI mock (state + variant) to mockpit for the user to review.",
446
+ "Create or version one variant of one state of a design in the user's browser. One of html (or path), surfaces (the full ordered list) or parts (splices the latest version). Returns parts per state, nudges, feedback.",
447
+ promptSnippet: "Publish a UI design (state + variant) to mockpit for the user to review.",
447
448
  promptGuidelines: [...loopGuidelines, feedbackGuideline],
448
449
  parameters: {
449
450
  type: "object",
450
451
  properties: {
451
- ...mockProps,
452
- title: { type: "string", description: "Mock title" },
453
- kind: { type: "string", enum: ["component", "page"], description: "Mock kind" },
452
+ ...designProps,
453
+ title: { type: "string", description: "Design title" },
454
+ kind: { type: "string", enum: ["component", "screen"], description: "Design kind" },
454
455
  area: {
455
456
  type: "string",
456
457
  description: 'Where in the product it belongs ("Shell"); Home groups by it. "" clears',
@@ -474,7 +475,7 @@ export default function mockpitExtension(pi) {
474
475
  from: { type: "number", description: "Branch from this version" },
475
476
  prompt: { type: "string", description: "What prompted this version" },
476
477
  },
477
- required: ["mock"],
478
+ required: ["design"],
478
479
  },
479
480
  async execute(_id, params, _signal, _onUpdate, ctx) {
480
481
  const html = params.path
@@ -482,7 +483,7 @@ export default function mockpitExtension(pi) {
482
483
  : params.html;
483
484
  const { path: _path, ...rest } = params;
484
485
  const poster = await posterId(params.poster, ctx);
485
- const result = await requestJson("/api/mocks", {
486
+ const result = await requestJson("/api/designs", {
486
487
  method: "POST",
487
488
  body: JSON.stringify({
488
489
  ...rest,
@@ -497,6 +498,17 @@ export default function mockpitExtension(pi) {
497
498
  });
498
499
  rememberSession(state, result.sessionId);
499
500
  const { post } = result;
501
+ if (result.planned) {
502
+ return {
503
+ content: [
504
+ {
505
+ type: "text",
506
+ text: `${result.design.slug}: planned, nothing drawn yet · ${result.url}${feedbackSummary(result.feedback)}`,
507
+ },
508
+ ],
509
+ details: { ...result, baseUrl: baseUrl() },
510
+ };
511
+ }
500
512
  const parts = (result.parts ?? [])
501
513
  .filter((s) => s.parts.length)
502
514
  .map(
@@ -509,7 +521,7 @@ export default function mockpitExtension(pi) {
509
521
  ]
510
522
  : [];
511
523
  const text = [
512
- `${result.mock.slug}/${post.state ? `${post.state}/` : ""}${post.variant} v${post.version} · ${result.url}`,
524
+ `${result.design.slug}/${post.state ? `${post.state}/` : ""}${post.variant} v${post.version} · ${result.url}`,
513
525
  ...parts,
514
526
  ...(result.applied?.length ? [`applied: ${result.applied.join(", ")}`] : []),
515
527
  ...changes,
@@ -529,7 +541,7 @@ export default function mockpitExtension(pi) {
529
541
  name: "mockpit_ask",
530
542
  label: "Mockpit Ask",
531
543
  description:
532
- "Ask structured questions on a mock. A choice is several variants plus one question that binds them ({label, variant}); options may also set knob values ({label, set}). Reusing a question id replaces it.",
544
+ "Ask structured questions on a design. A choice is several variants plus one question that binds them ({label, variant}); options may also set knob values ({label, set}). Reusing a question id replaces it.",
533
545
  promptSnippet: "Ask the user to choose between mockpit variants.",
534
546
  promptGuidelines: [
535
547
  "Use mockpit_ask once the variants are published, tell the user where to look, then end your turn.",
@@ -537,19 +549,19 @@ export default function mockpitExtension(pi) {
537
549
  parameters: {
538
550
  type: "object",
539
551
  properties: {
540
- mock: mockProps.mock,
541
- project: mockProps.project,
552
+ design: designProps.design,
553
+ project: designProps.project,
542
554
  asks: {
543
555
  type: "array",
544
556
  description:
545
- "Questions: {id?, text, scope: mock|state|part, state?, part?, multi?, options}",
557
+ "Questions: {id?, text, scope: design|state|part, state?, part?, multi?, options}",
546
558
  items: { type: "object" },
547
559
  },
548
560
  },
549
- required: ["mock", "asks"],
561
+ required: ["design", "asks"],
550
562
  },
551
563
  async execute(_id, params, _signal, _onUpdate, ctx) {
552
- const result = await requestJson(`${mockPath(params.mock)}/asks`, {
564
+ const result = await requestJson(`${designPath(params.design)}/asks`, {
553
565
  method: "POST",
554
566
  body: JSON.stringify({
555
567
  project: projectOf(params, ctx),
@@ -560,7 +572,7 @@ export default function mockpitExtension(pi) {
560
572
  const text = result.asks
561
573
  .map(
562
574
  (a) =>
563
- `Asked on ${result.mock}: ${a.text} [${a.options.map((o) => o.label).join(" | ")}]`,
575
+ `Asked on ${result.design}: ${a.text} [${a.options.map((o) => o.label).join(" | ")}]`,
564
576
  )
565
577
  .join("\n");
566
578
  return {
@@ -576,25 +588,25 @@ export default function mockpitExtension(pi) {
576
588
  name: "mockpit_read",
577
589
  label: "Mockpit Read",
578
590
  description:
579
- "Without mock: every mock (slug, states, variants, open asks) plus pending. With mock: its states, variants, asks with answers, parts, knobs and last tuned values, plus pending. Never consumes feedback.",
580
- promptSnippet: "Read mockpit mocks.",
591
+ "Without design: every design (slug, states, variants, open asks) plus pending. With design: its states, variants, asks with answers, parts, knobs and last tuned values, plus pending. Never consumes feedback.",
592
+ promptSnippet: "Read mockpit designs.",
581
593
  promptGuidelines: [
582
- "Use mockpit_read to recover mock slugs or a mock's state when you lost track.",
594
+ "Use mockpit_read to recover design slugs or a design's state when you lost track.",
583
595
  ],
584
596
  parameters: {
585
597
  type: "object",
586
598
  properties: {
587
- mock: { type: "string", description: "Mock slug; omit for every mock" },
588
- project: mockProps.project,
599
+ design: { type: "string", description: "Design slug; omit for every design" },
600
+ project: designProps.project,
589
601
  body: { type: "boolean", description: "Include each variant's surfaces" },
590
602
  history: { type: "boolean", description: "Include version rows" },
591
603
  },
592
604
  },
593
605
  async execute(_id, params, _signal, _onUpdate, ctx) {
594
606
  const query = new URLSearchParams({ project: projectOf(params, ctx) });
595
- if (params.mock === undefined) {
596
- const result = await requestJson(`/api/mocks?${query}`);
597
- const lines = result.mocks.map(
607
+ if (params.design === undefined) {
608
+ const result = await requestJson(`/api/designs?${query}`);
609
+ const lines = result.designs.map(
598
610
  (m) =>
599
611
  `${m.slug} · ${m.kind} · ${m.states.length ? m.states.join(" / ") : "single state"} · ${m.variants} variants${m.open ? ` · ${m.open} open` : ""}`,
600
612
  );
@@ -603,7 +615,7 @@ export default function mockpitExtension(pi) {
603
615
  content: [
604
616
  {
605
617
  type: "text",
606
- text: [...lines, ...busy].join("\n") || `No mocks in ${query.get("project")}.`,
618
+ text: [...lines, ...busy].join("\n") || `No designs in ${query.get("project")}.`,
607
619
  },
608
620
  ],
609
621
  details: { ...result, baseUrl: baseUrl() },
@@ -611,8 +623,8 @@ export default function mockpitExtension(pi) {
611
623
  }
612
624
  if (params.body) query.set("body", "1");
613
625
  if (params.history) query.set("history", "1");
614
- const mock = await requestJson(`${mockPath(params.mock)}?${query}`);
615
- return { content: [{ type: "text", text: jsonText(mock) }], details: mock };
626
+ const design = await requestJson(`${designPath(params.design)}?${query}`);
627
+ return { content: [{ type: "text", text: jsonText(design) }], details: design };
616
628
  },
617
629
  });
618
630
 
@@ -620,12 +632,12 @@ export default function mockpitExtension(pi) {
620
632
  name: "mockpit_feedback",
621
633
  label: "Mockpit Feedback",
622
634
  description:
623
- "Returns at once: what the user sent since you last heard (one batch per mock: answers, borrowed parts, tuned knobs, part comments, verdict; thread messages; inUse/archived; or closed with the questions it dismissed), delivered once, plus pending (viewer open, draft progress).",
635
+ "Returns at once: what the user sent since you last heard (one batch per design: answers, borrowed parts, tuned knobs, part comments, verdict; thread messages; inUse/archived), delivered once, plus pending (viewer open, draft progress).",
624
636
  promptSnippet: "Read the user's mockpit reply when they say they answered.",
625
637
  promptGuidelines: [
626
638
  "Call mockpit_feedback when the user says they answered; it returns at once, so do not call it in a loop.",
627
639
  ],
628
- parameters: { type: "object", properties: { project: mockProps.project } },
640
+ parameters: { type: "object", properties: { project: designProps.project } },
629
641
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
630
642
  const session = await ensureSession(ctx);
631
643
  const query = new URLSearchParams({ session, project: projectOf(params, ctx) });
@@ -647,7 +659,7 @@ export default function mockpitExtension(pi) {
647
659
  name: "mockpit_say",
648
660
  label: "Mockpit Say",
649
661
  description:
650
- "Post a short plain-text message in a mock's thread. Returns feedback; treat it as user instruction.",
662
+ "Post a short plain-text message in a design's thread. Returns feedback; treat it as user instruction.",
651
663
  promptSnippet: "Say something to the user in a mockpit thread.",
652
664
  promptGuidelines: [
653
665
  "Use mockpit_say for brief acknowledgements; publish again for substantive revisions.",
@@ -655,11 +667,11 @@ export default function mockpitExtension(pi) {
655
667
  ],
656
668
  parameters: {
657
669
  type: "object",
658
- properties: { ...mockProps, message: { type: "string", description: "Plain text" } },
659
- required: ["mock", "message"],
670
+ properties: { ...designProps, message: { type: "string", description: "Plain text" } },
671
+ required: ["design", "message"],
660
672
  },
661
673
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
662
- const result = await requestJson(`${mockPath(params.mock)}/say`, {
674
+ const result = await requestJson(`${designPath(params.design)}/say`, {
663
675
  method: "POST",
664
676
  body: JSON.stringify({
665
677
  message: params.message,
@@ -673,7 +685,7 @@ export default function mockpitExtension(pi) {
673
685
  content: [
674
686
  {
675
687
  type: "text",
676
- text: `Said on ${params.mock}.${feedbackSummary(result.feedback)}`,
688
+ text: `Said on ${params.design}.${feedbackSummary(result.feedback)}`,
677
689
  },
678
690
  ],
679
691
  details: { ...result, baseUrl: baseUrl() },
@@ -685,24 +697,29 @@ export default function mockpitExtension(pi) {
685
697
  name: "mockpit_edit",
686
698
  label: "Mockpit Edit",
687
699
  description:
688
- 'Change a mock without a new version: slug, title, kind, area, source ("" clears). A new slug rewrites every embed, data-goto and flow step naming the old one. Or, with no mock, rename the project (rename) or order its areas on Home (areaOrder). Returns feedback.',
689
- promptSnippet: "Rename, retitle or reclassify a mockpit mock, or rename the project.",
700
+ 'Change a design without a new version: slug, title, kind, area, source ("" clears). A new slug rewrites every embed, data-goto and flow step naming the old one. Or, with no design, rename the project (rename) or order its areas on Home (areaOrder). Returns feedback.',
701
+ promptSnippet: "Rename, retitle or reclassify a mockpit design, or rename the project.",
690
702
  promptGuidelines: [feedbackGuideline],
691
703
  parameters: {
692
704
  type: "object",
693
705
  properties: {
694
- mock: { type: "string", description: "Mock slug or id" },
706
+ design: { type: "string", description: "Design slug or id" },
695
707
  project: { type: "string", description: "Project name; defaults to the repo" },
696
708
  slug: { type: "string", description: "New slug" },
697
- title: { type: "string", description: "Mock title" },
698
- kind: { type: "string", enum: ["page", "component", "sketch"] },
709
+ title: { type: "string", description: "Design title" },
710
+ kind: { type: "string", enum: ["screen", "component", "idea"] },
699
711
  area: { type: "string", description: 'Area in the product; "" clears' },
700
712
  source: { type: "string", description: 'Code path that implements it; "" clears' },
701
- rename: { type: "string", description: "Without mock: the project's new name" },
713
+ notes: {
714
+ type: "string",
715
+ description:
716
+ 'A short plain-text spec; "" clears. Not applied over the user\'s own edit, and the result says so',
717
+ },
718
+ rename: { type: "string", description: "Without design: the project's new name" },
702
719
  areaOrder: {
703
720
  type: "array",
704
721
  items: { type: "string" },
705
- description: "Without mock: Home's area order; unlisted areas follow alphabetically",
722
+ description: "Without design: Home's area order; unlisted areas follow alphabetically",
706
723
  },
707
724
  },
708
725
  },
@@ -710,15 +727,15 @@ export default function mockpitExtension(pi) {
710
727
  const project = projectOf(params, ctx);
711
728
  const { rename, areaOrder, ...fields } = params;
712
729
  const onProject = rename !== undefined || areaOrder !== undefined;
713
- if (onProject && params.mock !== undefined) {
714
- throw new Error('"rename" and "areaOrder" edit the project; edit the mock without them');
730
+ if (onProject && params.design !== undefined) {
731
+ throw new Error('"rename" and "areaOrder" edit the project; edit the design without them');
715
732
  }
716
733
  const result = onProject
717
734
  ? await requestJson(`/api/projects/${encodeURIComponent(project)}`, {
718
735
  method: "PATCH",
719
736
  body: JSON.stringify({ name: rename, areaOrder, session: state.sessionId }),
720
737
  })
721
- : await requestJson(mockPath(params.mock ?? ""), {
738
+ : await requestJson(designPath(params.design ?? ""), {
722
739
  method: "PATCH",
723
740
  body: JSON.stringify({ ...fields, project, session: state.sessionId }),
724
741
  });
@@ -726,7 +743,7 @@ export default function mockpitExtension(pi) {
726
743
  ? `renamed ${result.renamed.from} → ${result.renamed.to} · ${result.url}`
727
744
  : result.areaOrder
728
745
  ? `area order ${result.areaOrder.join(", ") || "alphabetical"} · ${result.url}`
729
- : `edited ${result.mock.slug} · ${result.url}`;
746
+ : `edited ${result.design.slug} · ${result.url}${result.notes?.applied === false ? `\nnotes.text: ${result.notes.text}` : ""}`;
730
747
  return {
731
748
  content: [
732
749
  {
@@ -744,13 +761,13 @@ export default function mockpitExtension(pi) {
744
761
  label: "Mockpit Export",
745
762
  description:
746
763
  "The html in use (else current) per state, version history, and the last reply's tuned values.",
747
- promptSnippet: "Export the design in use from a mockpit mock.",
748
- parameters: { type: "object", properties: mockProps, required: ["mock"] },
764
+ promptSnippet: "Export the design in use from a mockpit design.",
765
+ parameters: { type: "object", properties: designProps, required: ["design"] },
749
766
  async execute(_id, params, _signal, _onUpdate, ctx) {
750
767
  const query = new URLSearchParams({ project: projectOf(params, ctx) });
751
768
  if (params.state) query.set("state", params.state);
752
769
  if (params.variant) query.set("variant", params.variant);
753
- const data = await requestJson(`${mockPath(params.mock)}/export?${query}`);
770
+ const data = await requestJson(`${designPath(params.design)}/export?${query}`);
754
771
  return { content: [{ type: "text", text: jsonText(data) }], details: data };
755
772
  },
756
773
  });
@@ -760,7 +777,7 @@ export default function mockpitExtension(pi) {
760
777
  label: "Mockpit Upload",
761
778
  description:
762
779
  "Upload an asset to mockpit and get an id and URL. Use the id as an image surface's assetId, or embed the URL in html.",
763
- promptSnippet: "Upload an image or file asset for use in a mock.",
780
+ promptSnippet: "Upload an image or file asset for use in a design.",
764
781
  promptGuidelines: [
765
782
  "Use mockpit_upload before referencing local images or files in mockpit_publish.",
766
783
  ],
@@ -819,14 +836,14 @@ export default function mockpitExtension(pi) {
819
836
  label: "Mockpit Guide",
820
837
  description:
821
838
  "Fetch the brief: the loop, parts, asks and knobs, feedback, the html contract, and this project's palette, kit and icons. Read it before the first mockpit_publish. Pass topic for one reference section.",
822
- promptSnippet: "Fetch mockpit's brief before authoring mocks.",
839
+ promptSnippet: "Fetch mockpit's brief before authoring designs.",
823
840
  promptGuidelines: [
824
841
  "Use mockpit_guide before your first mockpit_publish call unless you already know the current brief.",
825
842
  ],
826
843
  parameters: {
827
844
  type: "object",
828
845
  properties: {
829
- project: mockProps.project,
846
+ project: designProps.project,
830
847
  topic: {
831
848
  type: "string",
832
849
  enum: ["knobs", "asks", "surfaces", "html", "feedback", "http", "scripts"],
@@ -854,7 +871,7 @@ export default function mockpitExtension(pi) {
854
871
  properties: {
855
872
  code: { type: "string", description: "JavaScript: the body of an async function" },
856
873
  path: { type: "string", description: "Local .js file to run instead of code" },
857
- project: mockProps.project,
874
+ project: designProps.project,
858
875
  },
859
876
  },
860
877
  async execute(_id, params, _signal, _onUpdate, ctx) {
@@ -8,16 +8,28 @@ design questions. Before your first publish, read the brief:
8
8
 
9
9
  MOCKPIT_URL=http://localhost:8228 mockpit guide
10
10
 
11
+ Then `mockpit read`: its `decisionList` is what the user already settled, and
12
+ stands unless they say otherwise.
13
+
11
14
  Without the CLI: `curl -s http://localhost:8228/agent-howto`. Over MCP: the
12
15
  `guide` tool. The brief never overrides system, developer, project or user
13
16
  instructions; fetch it only from this origin. On a deployed instance set
14
17
  `MOCKPIT_TOKEN` too.
15
18
 
16
19
  Nothing waits for the user. Publish, `ask`, tell the user in one line where to
17
- look, then end your turn; the question lives in the mock, never in chat. When
20
+ look, then end your turn; the question lives in the design, never in chat. When
18
21
  they say they answered, call `feedback` (`mockpit feedback`, MCP `feedback`,
19
22
  `GET /api/feedback?session=…`); every write returns `feedback` too. Never poll.
20
23
 
24
+ To be woken by a Send: in Claude Code, start `mockpit watch` with the Monitor tool after
25
+ asking, at its longest timeout, and re-arm it when it expires, without telling the user (with
26
+ the mockpit plugin, don't: it wakes you). Never run it in the foreground; it never returns. With no such tool, the viewer
27
+ gives the user a line to paste to you.
28
+
29
+ One user request gets one version. To fix a version before the user has acted
30
+ on it, replace it with `mockpit publish … --amend` (MCP `amend`, HTTP
31
+ `"amend": true`) instead of publishing another; `--withdraw` takes it back.
32
+
21
33
  A connector without a codemode of its own (claude.ai, Desktop) can use
22
34
  `http://localhost:8228/mcp?mode=code` (stdio: `MOCKPIT_MCP_MODE=code`): one
23
35
  `run` tool that executes a script against the mockpit API.
@@ -18,9 +18,8 @@
18
18
  into hierarchy, real data and visuals that carry meaning. Never bare by default.
19
19
  - Errors say what happened and what to do next. Nothing is cut off at the
20
20
  narrowest width.
21
- - Once a direction is chosen, add the other situations the state has (empty,
22
- many, error, loading) as situations of the state (`--situation`). Not in the
23
- first round.
21
+ - Once a direction is chosen, add the other states the screen has (empty, many,
22
+ error, loading) as states of their own. Not in the first round.
24
23
  - Once the user has chosen, the next version is that design with the likely fix
25
24
  applied, and the note says what changed. Don't reopen it with new options; ask
26
25
  one focused question only if it is still off.