mockpit 2.0.0 → 3.1.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 (90) hide show
  1. package/README.md +59 -223
  2. package/bin/demoData.js +2 -2
  3. package/bin/initDesign.js +17 -22
  4. package/bin/mockpit.js +626 -216
  5. package/bin/poster.js +434 -0
  6. package/dist/mcp/server.js +127 -29
  7. package/dist/server/answerAdditions.d.ts +7 -0
  8. package/dist/server/answerAdditions.js +76 -0
  9. package/dist/server/apiViews.d.ts +627 -385
  10. package/dist/server/apiViews.js +198 -81
  11. package/dist/server/app.d.ts +7 -2
  12. package/dist/server/app.js +3514 -984
  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 +640 -0
  18. package/dist/server/codeWorker.js +2 -2
  19. package/dist/server/contrast.d.ts +4 -0
  20. package/dist/server/contrast.js +157 -0
  21. package/dist/server/decisionList.d.ts +48 -0
  22. package/dist/server/decisionList.js +340 -0
  23. package/dist/server/decisions.d.ts +4 -4
  24. package/dist/server/decisions.js +9 -9
  25. package/dist/server/designGuide.d.ts +14 -7
  26. package/dist/server/designGuide.js +314 -88
  27. package/dist/server/embeds.d.ts +3 -1
  28. package/dist/server/embeds.js +31 -6
  29. package/dist/server/eventLog.d.ts +2 -2
  30. package/dist/server/eventLog.js +11 -11
  31. package/dist/server/events.d.ts +15 -6
  32. package/dist/server/feedbackBatch.d.ts +39 -13
  33. package/dist/server/feedbackBatch.js +129 -47
  34. package/dist/server/fills.d.ts +4 -0
  35. package/dist/server/fills.js +55 -1
  36. package/dist/server/guidance.d.ts +1 -1
  37. package/dist/server/guidance.js +2 -2
  38. package/dist/server/handoff.d.ts +4 -1
  39. package/dist/server/handoff.js +8 -2
  40. package/dist/server/icons.js +1 -1
  41. package/dist/server/index.js +39 -10
  42. package/dist/server/jump.d.ts +21 -0
  43. package/dist/server/jump.js +175 -0
  44. package/dist/server/kits.d.ts +2 -2
  45. package/dist/server/kits.js +54 -1
  46. package/dist/server/knobs.d.ts +3 -3
  47. package/dist/server/knobs.js +6 -6
  48. package/dist/server/mcpHttp.d.ts +7 -1
  49. package/dist/server/mcpHttp.js +29 -12
  50. package/dist/server/mcpSpec.d.ts +27 -27
  51. package/dist/server/mcpSpec.js +196 -56
  52. package/dist/server/parts.js +9 -1
  53. package/dist/server/run.d.ts +1 -1
  54. package/dist/server/run.js +47 -17
  55. package/dist/server/runApi.d.ts +3 -3
  56. package/dist/server/runApi.js +34 -21
  57. package/dist/server/sqlStore.d.ts +148 -103
  58. package/dist/server/sqlStore.js +1052 -181
  59. package/dist/server/standings.d.ts +25 -0
  60. package/dist/server/standings.js +113 -0
  61. package/dist/server/surfacePage.d.ts +8 -9
  62. package/dist/server/surfacePage.js +423 -100
  63. package/dist/server/system.d.ts +234 -0
  64. package/dist/server/system.js +1508 -0
  65. package/dist/server/systemChecks.d.ts +15 -0
  66. package/dist/server/systemChecks.js +224 -0
  67. package/dist/server/systemSpecimen.d.ts +2 -0
  68. package/dist/server/systemSpecimen.js +49 -0
  69. package/dist/server/themes.d.ts +9 -7
  70. package/dist/server/themes.js +43 -90
  71. package/dist/server/types.d.ts +266 -93
  72. package/dist/server/types.js +76 -79
  73. package/extensions/mockpit.js +89 -72
  74. package/guide/AGENT_SETUP.md +15 -1
  75. package/guide/packs/design-craft.md +2 -3
  76. package/guide/packs/foundations.md +35 -0
  77. package/guide/topics/asks.md +108 -63
  78. package/guide/topics/feedback.md +129 -120
  79. package/guide/topics/html.md +18 -25
  80. package/guide/topics/http.md +43 -41
  81. package/guide/topics/knobs.md +7 -10
  82. package/guide/topics/scripts.md +22 -33
  83. package/guide/topics/surfaces.md +39 -48
  84. package/guide/topics/system.md +157 -0
  85. package/opencode/mockpit.js +120 -0
  86. package/opencode/package.json +8 -0
  87. package/package.json +15 -14
  88. package/skills/launch-video/SKILL.md +11 -11
  89. package/skills/mockpit/SKILL.md +33 -26
  90. package/viewer/dist/index.html +10 -9
@@ -48,8 +48,16 @@ export function partsInHtml(html) {
48
48
  }
49
49
  return [...byName.values()];
50
50
  }
51
+ // Cached per surfaces array, which a stored post never mutates: a design's view
52
+ // asks for every variant's parts on every read. Callers must not change the result.
53
+ const bySurfaces = new WeakMap();
51
54
  export function partsInSurfaces(surfaces) {
52
- return mergeParts(surfaces.flatMap((s) => (s.kind === "html" ? [partsInHtml(s.html)] : [])));
55
+ const hit = bySurfaces.get(surfaces);
56
+ if (hit)
57
+ return hit;
58
+ const parts = mergeParts(surfaces.flatMap((s) => (s.kind === "html" ? [partsInHtml(s.html)] : [])));
59
+ bySurfaces.set(surfaces, parts);
60
+ return parts;
53
61
  }
54
62
  // Union several part lists by name, first occurrence first.
55
63
  export function mergeParts(lists) {
@@ -77,6 +77,6 @@ export interface RunDeps {
77
77
  status: number;
78
78
  }>;
79
79
  }
80
- export declare const RUN_UNAVAILABLE = "run is not available on this deployment: it has no script sandbox. Use the mock tools (or REST) instead";
80
+ export declare const RUN_UNAVAILABLE = "run is not available on this deployment: it has no script sandbox. Use the design tools (or REST) instead";
81
81
  export declare function cut(s: string, max: number): string;
82
82
  export declare function createRunFlow(deps: RunDeps): (body: any, ctx: FlowContext) => Promise<FlowResult>;
@@ -26,7 +26,7 @@ export const DEFAULT_RUN_LIMITS = {
26
26
  maxInflight: 4,
27
27
  maxRuns: 4,
28
28
  };
29
- export const RUN_UNAVAILABLE = "run is not available on this deployment: it has no script sandbox. Use the mock tools (or REST) instead";
29
+ export const RUN_UNAVAILABLE = "run is not available on this deployment: it has no script sandbox. Use the design tools (or REST) instead";
30
30
  const text = (v, max) => typeof v === "string" && v.trim() ? v.trim().slice(0, max) : undefined;
31
31
  const plain = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
32
32
  // Keep the head and the tail: the start says what a value is, the end is where
@@ -40,7 +40,7 @@ export function cut(s, max) {
40
40
  }
41
41
  function writeSummary(body) {
42
42
  const post = body?.post;
43
- const slug = body?.mock?.slug ?? "?";
43
+ const slug = body?.design?.slug ?? "?";
44
44
  if (!post)
45
45
  return slug;
46
46
  return `${slug}/${post.state ? `${post.state}/` : ""}${post.variant} v${post.version}`;
@@ -150,28 +150,35 @@ export function createRunFlow(deps) {
150
150
  return [await deps.guide(await projectOf()), "brief"];
151
151
  if (!isGuideTopic(topic))
152
152
  throw new Error(unknownTopicMessage(String(topic)));
153
- return [await deps.guide("", topic), String(topic)];
153
+ return [
154
+ await deps.guide(topic === "system" ? await projectOf() : "", topic),
155
+ String(topic),
156
+ ];
154
157
  },
155
- async read([mock, opts]) {
156
- if ((mock === undefined || mock === null) && plain(opts).tree === true) {
158
+ async read([design, opts]) {
159
+ if (plain(opts).decisions === true) {
160
+ const value = await take(flows.decisions({ project: await projectOf() }));
161
+ return [value, `${value.total} decisions`];
162
+ }
163
+ if ((design === undefined || design === null) && plain(opts).tree === true) {
157
164
  const value = await take(flows.tree({ project: await projectOf() }));
158
- return [value, `${value.tree.length} mocks`];
165
+ return [value, `${value.tree.length} designs`];
159
166
  }
160
- if (mock === undefined || mock === null) {
167
+ if (design === undefined || design === null) {
161
168
  const value = await take(flows.list({ project: await projectOf() }));
162
- return [value, `${value.mocks.length} mocks`];
169
+ return [value, `${value.designs.length} designs`];
163
170
  }
164
171
  const o = plain(opts);
165
- const value = await take(flows.get(mock, { project, body: o.body === true, history: o.history === true }));
166
- return [value, String(value?.slug ?? mock)];
172
+ const value = await take(flows.get(design, { project, body: o.body === true, history: o.history === true }));
173
+ return [value, String(value?.slug ?? design)];
167
174
  },
168
175
  async publish([v]) {
169
176
  const value = await take(flows.publish({ ...(await mine(plain(v))), agent }, flowCtx));
170
177
  return [value, writeSummary(value)];
171
178
  },
172
- async ask([mock, asks]) {
173
- const value = await take(flows.ask(mock, await mine({ asks }), flowCtx));
174
- return [value, `${value.mock} ${value.asks.length} ask(s)`];
179
+ async ask([design, asks]) {
180
+ const value = await take(flows.ask(design, await mine({ asks }), flowCtx));
181
+ return [value, `${value.design} ${value.asks.length} ask(s)`];
175
182
  },
176
183
  async feedback() {
177
184
  const value = await take(flows.feedback(await mine({}), flowCtx));
@@ -180,8 +187,27 @@ export function createRunFlow(deps) {
180
187
  },
181
188
  async say([v, message]) {
182
189
  const r = plain(v);
183
- await take(flows.say(r.mock, { ...(await mine(r)), message }, flowCtx));
184
- return [null, String(r.mock ?? "?")];
190
+ await take(flows.say(r.design, { ...(await mine(r)), message }, flowCtx));
191
+ return [null, String(r.design ?? "?")];
192
+ },
193
+ async move([v]) {
194
+ const r = plain(v);
195
+ const value = await take(flows.move(r.design, await mine(r), flowCtx));
196
+ return [value, `${value.variants.length} moved`];
197
+ },
198
+ async system([v]) {
199
+ const given = plain(v ?? {});
200
+ const named = project ?? (typeof given.project === "string" ? given.project : undefined);
201
+ const writing = Object.keys(given).some((k) => !["session", "project", "agent"].includes(k));
202
+ // A write lands on a project the script names, never on whichever is first.
203
+ if (writing && !named)
204
+ throw new Error("system writes need a project: pass {project} to run, or run in a session that has one");
205
+ const r = await mine(given);
206
+ const value = await take(flows.system(named ?? (await projectOf()), r, flowCtx));
207
+ return [
208
+ value,
209
+ value.changed?.length ? `r${value.rev}: ${value.changed.join("; ")}` : `r${value.rev}`,
210
+ ];
185
211
  },
186
212
  async edit([v]) {
187
213
  const r = await mine(plain(v));
@@ -206,8 +232,12 @@ export function createRunFlow(deps) {
206
232
  },
207
233
  async export([v]) {
208
234
  const r = plain(v);
209
- const value = await take(flows.exportMock(r.mock, { ...r, project }, flowCtx));
210
- return [value, `${value.mock} ${value.states.length} state(s)`];
235
+ if (r.design === undefined) {
236
+ const value = await take(flows.exportSystem(r.project ?? project ?? (await projectOf())));
237
+ return [value, `${value.project} system r${value.rev}`];
238
+ }
239
+ const value = await take(flows.exportDesign(r.design, { ...r, project }, flowCtx));
240
+ return [value, `${value.design} ${value.states.length} state(s)`];
211
241
  },
212
242
  };
213
243
  const call = (name, argsJson) => {
@@ -1,6 +1,6 @@
1
- export declare const RUN_API = "// The body of an async function, in plain JavaScript (no TypeScript syntax).\n// `await mockpit.*`, `return` a JSON value, print() adds an output line.\n// No network, timers, imports or process. Session, project and agent are fixed\n// for the run; pass the returned session to the next run.\ntype Ref = { mock: string; state?: string; variant?: string };\ntype Knobs = Record<string, unknown>; // path \u2192 [default,min,max,step] | boolean | {type:\"select\",options} | \"#hex\"\ntype Surface =\n | { kind: \"html\"; html: string; kits?: string[] }\n | { kind: \"markdown\"; markdown: string }\n | { kind: \"diff\"; patch?: string; files?: { filename: string; before: string; after: string }[]; layout?: \"unified\" | \"split\" }\n | { kind: \"image\"; assetId: string; alt?: string; caption?: string }\n | { kind: \"terminal\"; text: string; cols?: number; title?: string }\n | { kind: \"mermaid\"; mermaid: string }\n | { kind: \"json\"; data: unknown; title?: string }\n | { kind: \"code\"; code: string; language?: string; lineStart?: number };\ntype Ask = {\n id?: string; text: string; scope?: \"mock\" | \"state\" | \"part\"; state?: string; part?: string; multi?: boolean;\n options: { id?: string; label: string; variant?: string; set?: Knobs; recommended?: boolean }[];\n};\ntype Written = {\n post: { state: string | null; variant: string; version: number }; url: string;\n parts: { state: string | null; parts: { name: string }[] }[];\n partChanges?: { vanished: string[]; renamed: { from: string; to: string }[] };\n nudges?: string[]; suggestedAsk?: Ask; suggestedFillAsks?: Ask[]; // send with ask when nudged\n};\ntype Feedback = {\n mock: string | null;\n reply: null | { asks: { ask: string; chosen: { id: string; label: string; other?: true }[]; note?: string }[]; borrowed: Record<string, string>; tuned: Knobs; comments: { part: string | null; text: string }[]; verdict?: { kind: \"use\" | \"revise\"; state: string | null; variant: string; version?: number } };\n messages: { text: string; state: string | null; variant: string | null }[];\n inUse: { state: string | null; variant: string; version: number }[]; archived: { state: string | null; variant: string }[];\n closed?: true; dismissed?: { ask: string; text: string }[];\n};\ntype Pending = { mock: string; closed?: true; viewerOpen: boolean; draft: null | { answered: number; of: number; comments: number; touchedAt: string } };\ndeclare const mockpit: {\n guide(topic?: \"knobs\" | \"asks\" | \"surfaces\" | \"html\" | \"feedback\" | \"http\" | \"scripts\"): Promise<string>;\n /** No mock: every mock, the flows, plus pending. With one: its states, variants, asks, parts, knobs. */\n read(mock?: string, opts?: { body?: boolean; history?: boolean }): Promise<unknown>;\n /** Creates or versions (mock, state, variant). One of: html; surfaces, the full ordered list\n * ({id} alone keeps that surface, a missing id is removed); parts {\"name\"|\"name#key\": outer html};\n * fills {\"part#fill\": outer html}, replacing or adding one fill of a part. */\n publish(v: Ref & { situation?: string; title?: string; kind?: \"page\" | \"component\" | \"sketch\"; area?: string; source?: string; html?: string; poster?: string; surfaces?: (Surface | { id: string })[]; parts?: Record<string, string>; fills?: Record<string, string>; knobs?: Knobs; variantKnobs?: Knobs; from?: number; prompt?: string; note?: string; widths?: number[]; flow?: string; step?: number }): Promise<Written>;\n ask(mock: string, asks: Ask[]): Promise<{ asks: { id: string; text: string }[]; url: string; nudges?: string[] }>;\n /** Never waits: what the user sent since you last heard, and what they are doing now. */\n feedback(): Promise<{ feedback: Feedback[]; pending: Pending[] }>;\n say(v: Ref, message: string): Promise<void>;\n /** No new version; \"\" clears. { rename } or { areaOrder } without mock edits the project. */\n edit(v: { mock?: string; slug?: string; title?: string; kind?: string; area?: string; source?: string; rename?: string; areaOrder?: string[] }): Promise<{ url: string }>;\n export(v: Ref): Promise<{ states: { state: string | null; variant: string; version: number; html: string; allFills?: string }[]; reply: unknown }>;\n /** data: base64 bytes. Use id as an image surface's assetId. */\n upload(data: string, opts: { contentType: string; filename?: string; kind?: \"image\" | \"file\" }): Promise<{ id: string; url: string }>;\n};\ndeclare function print(...values: unknown[]): void;";
2
- export declare const RUN_FUNCTIONS: readonly ["guide", "read", "publish", "ask", "feedback", "say", "edit", "export", "upload"];
1
+ export declare const RUN_API = "// The body of an async function, in plain JavaScript (no TypeScript syntax).\n// `await mockpit.*`, `return` a JSON value, print() adds an output line.\n// No network, timers, imports or process. Session, project and agent are fixed\n// for the run; pass the returned session to the next run.\ntype Ref = { design: string; state?: string; variant?: string };\ntype Knobs = Record<string, unknown>; // path \u2192 [default,min,max,step] | boolean | {type:\"select\",options} | \"#hex\"\ntype Surface =\n | { kind: \"html\"; html: string; kits?: string[] }\n | { kind: \"markdown\"; markdown: string }\n | { kind: \"diff\"; patch?: string; files?: { filename: string; before: string; after: string }[]; layout?: \"unified\" | \"split\" }\n | { kind: \"image\"; assetId: string; alt?: string; caption?: string }\n | { kind: \"terminal\"; text: string; cols?: number; title?: string }\n | { kind: \"mermaid\"; mermaid: string }\n | { kind: \"json\"; data: unknown; title?: string }\n | { kind: \"code\"; code: string; language?: string; lineStart?: number };\ntype Ask = {\n id?: string; text: string; scope?: \"design\" | \"state\" | \"part\"; state?: string; part?: string; multi?: boolean;\n options: { id?: string; label: string; variant?: string; set?: Knobs; palette?: string; recommended?: boolean }[];\n};\ntype Written = {\n post?: { state: string | null; variant: string; version: number }; planned?: true; url: string;\n parts?: { state: string | null; parts: { name: string }[] }[];\n partChanges?: { vanished: string[]; renamed: { from: string; to: string }[] };\n nudges?: string[]; suggestedAsk?: Ask; suggestedFillAsks?: Ask[]; // send with ask when nudged\n};\ntype Feedback = {\n design: string | null;\n reply: null | { asks: { ask: string; chosen: { id: string; label: string; other?: true }[]; comment?: string }[]; borrowed: Record<string, string>; tuned: Knobs; comments: { part: string | null; text: string }[]; verdict?: { kind: \"use\" | \"revise\"; state: string | null; variant: string; version?: number } };\n messages: { text: string; state: string | null; variant: string | null }[];\n inUse: { state: string | null; variant: string; version: number }[]; archived: { state: string | null; variant: string }[];\n system?: { rev: number; changes: string[] };\n};\ntype Pending = { design: string; archived?: true; viewerOpen: boolean; draft: null | { answered: number; of: number; comments: number; touchedAt: string } };\ndeclare const mockpit: {\n guide(topic?: \"system\" | \"knobs\" | \"asks\" | \"surfaces\" | \"html\" | \"feedback\" | \"http\" | \"scripts\"): Promise<string>;\n /** Read first: decisionList is what the user already settled; it stands unless they say otherwise.\n * No design: every design, the flows, plus pending. With one: its states, variants, asks, parts, knobs.\n * opts.decisions: only the whole list, when a read cut it. */\n read(design?: string, opts?: { body?: boolean; history?: boolean; decisions?: boolean }): Promise<unknown>;\n /** Creates or versions (design, state, variant). One of: html; surfaces, the full ordered list\n * ({id} alone keeps that surface, a missing id is removed); parts {\"name\"|\"name#key\": outer html};\n * fills {\"part#fill\": outer html}, replacing or adding one fill of a part.\n * None of them, with kind screen or component: plans it, for a later publish to draw. */\n publish(v: Ref & { title?: string; kind?: \"screen\" | \"component\" | \"idea\"; area?: string; source?: string; html?: string; poster?: string; surfaces?: (Surface | { id: string })[]; parts?: Record<string, string>; fills?: Record<string, string>; knobs?: Knobs; variantKnobs?: Knobs; from?: number; prompt?: string; description?: string; widths?: number[]; flow?: string; step?: number }): Promise<Written>;\n ask(design: string, asks: Ask[]): Promise<{ asks: { id: string; text: string }[]; url: string; nudges?: string[] }>;\n /** Never waits: what the user sent since you last heard, and what they are doing now. */\n feedback(): Promise<{ feedback: Feedback[]; pending: Pending[] }>;\n say(v: Ref, message: string): Promise<void>;\n /** to or toState moves variants there; archive hides them; restore brings them back. */\n move(v: Ref & { variants: string[]; to?: string; toState?: string; archive?: boolean; restore?: boolean }): Promise<{ variants: { state: string | null; variant: string; status: string }[]; skipped: { variant: string; why: string }[]; url: string }>;\n /** No new version; \"\" clears. { rename } or { areaOrder } without design edits the project.\n * notes: a short plain-text spec; not applied over the user's own edit (notes.applied says). */\n edit(v: { design?: string; slug?: string; title?: string; kind?: string; area?: string; source?: string; firstState?: string; notes?: string; rename?: string; areaOrder?: string[] }): Promise<{ url: string; notes?: { applied: boolean; text?: string } }>;\n /** The project's system: sheet, tokens, scales, palettes, project knobs. No argument reads it;\n * each key replaces its part (tokens, scales, palettes upsert by name). Applies to every design at once. */\n system(v?: { sheet?: string; classes?: { name: string; note: string }[]; fonts?: { family: string; href?: string }[]; tokens?: unknown[]; scales?: unknown[]; roles?: { name: string; note: string }[]; palettes?: unknown[]; pairs?: [string, string, number][]; as?: Record<string, string>; knobs?: Knobs; remove?: { tokens?: string[]; palettes?: string[]; scales?: string[] }; restore?: number }): Promise<{ rev: number; changed?: string[]; kept?: string[]; checks: string[]; url: string }>;\n export(v: Ref): Promise<{ states: { state: string | null; variant: string; version: number; html: string; allFills?: string }[]; reply: unknown }>;\n /** Without design: the project's system for code. css holds the default palette on :root and kept ones by [data-palette]. */\n export(v: {}): Promise<{ rev: number; sheet: string; css: string; tokens: unknown; knobs: Record<string, unknown>; additions: { knob: string; value: string; style: string }[]; components: { slug: string; purpose: string; source: string | null }[] }>;\n /** data: base64 bytes. Use id as an image surface's assetId. */\n upload(data: string, opts: { contentType: string; filename?: string; kind?: \"image\" | \"file\" }): Promise<{ id: string; url: string }>;\n};\ndeclare function print(...values: unknown[]): void;";
2
+ export declare const RUN_FUNCTIONS: readonly ["guide", "read", "publish", "ask", "feedback", "say", "move", "edit", "system", "export", "upload"];
3
3
  export type RunFunction = (typeof RUN_FUNCTIONS)[number];
4
- export declare const RUN_EXAMPLE = "const html = (tone) => `<section data-part=\"hero\" class=\"${tone}\"><h1>Writer</h1></section>`;\nawait mockpit.publish({ mock: \"writer\", variant: \"calm\", html: html(\"calm\") });\nawait mockpit.publish({ mock: \"writer\", variant: \"bold\", html: html(\"bold\") });\nconst { url } = await mockpit.ask(\"writer\", [{ id: \"tone\", text: \"Calm or bold?\",\n options: [{ label: \"Calm\", variant: \"calm\" }, { label: \"Bold\", variant: \"bold\" }] }]);\n// Tell the user where to look and end the turn; a later run reads mockpit.feedback().\nreturn url;";
4
+ export declare const RUN_EXAMPLE = "const html = (tone) => `<section data-part=\"hero\" class=\"${tone}\"><h1>Writer</h1></section>`;\nawait mockpit.publish({ design: \"writer\", variant: \"calm\", html: html(\"calm\") });\nawait mockpit.publish({ design: \"writer\", variant: \"bold\", html: html(\"bold\") });\nconst { url } = await mockpit.ask(\"writer\", [{ id: \"tone\", text: \"Calm or bold?\",\n options: [{ label: \"Calm\", variant: \"calm\" }, { label: \"Bold\", variant: \"bold\" }] }]);\n// Tell the user where to look and end the turn; a later run reads mockpit.feedback().\nreturn url;";
5
5
  export declare const RUN_DESCRIPTION: string;
6
6
  export declare const RUN_INSTRUCTIONS: string;
@@ -8,7 +8,7 @@ export const RUN_API = `// The body of an async function, in plain JavaScript (n
8
8
  // \`await mockpit.*\`, \`return\` a JSON value, print() adds an output line.
9
9
  // No network, timers, imports or process. Session, project and agent are fixed
10
10
  // for the run; pass the returned session to the next run.
11
- type Ref = { mock: string; state?: string; variant?: string };
11
+ type Ref = { design: string; state?: string; variant?: string };
12
12
  type Knobs = Record<string, unknown>; // path → [default,min,max,step] | boolean | {type:"select",options} | "#hex"
13
13
  type Surface =
14
14
  | { kind: "html"; html: string; kits?: string[] }
@@ -20,38 +20,49 @@ type Surface =
20
20
  | { kind: "json"; data: unknown; title?: string }
21
21
  | { kind: "code"; code: string; language?: string; lineStart?: number };
22
22
  type Ask = {
23
- id?: string; text: string; scope?: "mock" | "state" | "part"; state?: string; part?: string; multi?: boolean;
24
- options: { id?: string; label: string; variant?: string; set?: Knobs; recommended?: boolean }[];
23
+ id?: string; text: string; scope?: "design" | "state" | "part"; state?: string; part?: string; multi?: boolean;
24
+ options: { id?: string; label: string; variant?: string; set?: Knobs; palette?: string; recommended?: boolean }[];
25
25
  };
26
26
  type Written = {
27
- post: { state: string | null; variant: string; version: number }; url: string;
28
- parts: { state: string | null; parts: { name: string }[] }[];
27
+ post?: { state: string | null; variant: string; version: number }; planned?: true; url: string;
28
+ parts?: { state: string | null; parts: { name: string }[] }[];
29
29
  partChanges?: { vanished: string[]; renamed: { from: string; to: string }[] };
30
30
  nudges?: string[]; suggestedAsk?: Ask; suggestedFillAsks?: Ask[]; // send with ask when nudged
31
31
  };
32
32
  type Feedback = {
33
- mock: string | null;
34
- reply: null | { asks: { ask: string; chosen: { id: string; label: string; other?: true }[]; note?: string }[]; borrowed: Record<string, string>; tuned: Knobs; comments: { part: string | null; text: string }[]; verdict?: { kind: "use" | "revise"; state: string | null; variant: string; version?: number } };
33
+ design: string | null;
34
+ reply: null | { asks: { ask: string; chosen: { id: string; label: string; other?: true }[]; comment?: string }[]; borrowed: Record<string, string>; tuned: Knobs; comments: { part: string | null; text: string }[]; verdict?: { kind: "use" | "revise"; state: string | null; variant: string; version?: number } };
35
35
  messages: { text: string; state: string | null; variant: string | null }[];
36
36
  inUse: { state: string | null; variant: string; version: number }[]; archived: { state: string | null; variant: string }[];
37
- closed?: true; dismissed?: { ask: string; text: string }[];
37
+ system?: { rev: number; changes: string[] };
38
38
  };
39
- type Pending = { mock: string; closed?: true; viewerOpen: boolean; draft: null | { answered: number; of: number; comments: number; touchedAt: string } };
39
+ type Pending = { design: string; archived?: true; viewerOpen: boolean; draft: null | { answered: number; of: number; comments: number; touchedAt: string } };
40
40
  declare const mockpit: {
41
- guide(topic?: "knobs" | "asks" | "surfaces" | "html" | "feedback" | "http" | "scripts"): Promise<string>;
42
- /** No mock: every mock, the flows, plus pending. With one: its states, variants, asks, parts, knobs. */
43
- read(mock?: string, opts?: { body?: boolean; history?: boolean }): Promise<unknown>;
44
- /** Creates or versions (mock, state, variant). One of: html; surfaces, the full ordered list
41
+ guide(topic?: "system" | "knobs" | "asks" | "surfaces" | "html" | "feedback" | "http" | "scripts"): Promise<string>;
42
+ /** Read first: decisionList is what the user already settled; it stands unless they say otherwise.
43
+ * No design: every design, the flows, plus pending. With one: its states, variants, asks, parts, knobs.
44
+ * opts.decisions: only the whole list, when a read cut it. */
45
+ read(design?: string, opts?: { body?: boolean; history?: boolean; decisions?: boolean }): Promise<unknown>;
46
+ /** Creates or versions (design, state, variant). One of: html; surfaces, the full ordered list
45
47
  * ({id} alone keeps that surface, a missing id is removed); parts {"name"|"name#key": outer html};
46
- * fills {"part#fill": outer html}, replacing or adding one fill of a part. */
47
- publish(v: Ref & { situation?: string; title?: string; kind?: "page" | "component" | "sketch"; area?: string; source?: string; html?: string; poster?: string; surfaces?: (Surface | { id: string })[]; parts?: Record<string, string>; fills?: Record<string, string>; knobs?: Knobs; variantKnobs?: Knobs; from?: number; prompt?: string; note?: string; widths?: number[]; flow?: string; step?: number }): Promise<Written>;
48
- ask(mock: string, asks: Ask[]): Promise<{ asks: { id: string; text: string }[]; url: string; nudges?: string[] }>;
48
+ * fills {"part#fill": outer html}, replacing or adding one fill of a part.
49
+ * None of them, with kind screen or component: plans it, for a later publish to draw. */
50
+ publish(v: Ref & { title?: string; kind?: "screen" | "component" | "idea"; area?: string; source?: string; html?: string; poster?: string; surfaces?: (Surface | { id: string })[]; parts?: Record<string, string>; fills?: Record<string, string>; knobs?: Knobs; variantKnobs?: Knobs; from?: number; prompt?: string; description?: string; widths?: number[]; flow?: string; step?: number }): Promise<Written>;
51
+ ask(design: string, asks: Ask[]): Promise<{ asks: { id: string; text: string }[]; url: string; nudges?: string[] }>;
49
52
  /** Never waits: what the user sent since you last heard, and what they are doing now. */
50
53
  feedback(): Promise<{ feedback: Feedback[]; pending: Pending[] }>;
51
54
  say(v: Ref, message: string): Promise<void>;
52
- /** No new version; "" clears. { rename } or { areaOrder } without mock edits the project. */
53
- edit(v: { mock?: string; slug?: string; title?: string; kind?: string; area?: string; source?: string; rename?: string; areaOrder?: string[] }): Promise<{ url: string }>;
55
+ /** to or toState moves variants there; archive hides them; restore brings them back. */
56
+ move(v: Ref & { variants: string[]; to?: string; toState?: string; archive?: boolean; restore?: boolean }): Promise<{ variants: { state: string | null; variant: string; status: string }[]; skipped: { variant: string; why: string }[]; url: string }>;
57
+ /** No new version; "" clears. { rename } or { areaOrder } without design edits the project.
58
+ * notes: a short plain-text spec; not applied over the user's own edit (notes.applied says). */
59
+ edit(v: { design?: string; slug?: string; title?: string; kind?: string; area?: string; source?: string; firstState?: string; notes?: string; rename?: string; areaOrder?: string[] }): Promise<{ url: string; notes?: { applied: boolean; text?: string } }>;
60
+ /** The project's system: sheet, tokens, scales, palettes, project knobs. No argument reads it;
61
+ * each key replaces its part (tokens, scales, palettes upsert by name). Applies to every design at once. */
62
+ system(v?: { sheet?: string; classes?: { name: string; note: string }[]; fonts?: { family: string; href?: string }[]; tokens?: unknown[]; scales?: unknown[]; roles?: { name: string; note: string }[]; palettes?: unknown[]; pairs?: [string, string, number][]; as?: Record<string, string>; knobs?: Knobs; remove?: { tokens?: string[]; palettes?: string[]; scales?: string[] }; restore?: number }): Promise<{ rev: number; changed?: string[]; kept?: string[]; checks: string[]; url: string }>;
54
63
  export(v: Ref): Promise<{ states: { state: string | null; variant: string; version: number; html: string; allFills?: string }[]; reply: unknown }>;
64
+ /** Without design: the project's system for code. css holds the default palette on :root and kept ones by [data-palette]. */
65
+ export(v: {}): Promise<{ rev: number; sheet: string; css: string; tokens: unknown; knobs: Record<string, unknown>; additions: { knob: string; value: string; style: string }[]; components: { slug: string; purpose: string; source: string | null }[] }>;
55
66
  /** data: base64 bytes. Use id as an image surface's assetId. */
56
67
  upload(data: string, opts: { contentType: string; filename?: string; kind?: "image" | "file" }): Promise<{ id: string; url: string }>;
57
68
  };
@@ -64,18 +75,20 @@ export const RUN_FUNCTIONS = [
64
75
  "ask",
65
76
  "feedback",
66
77
  "say",
78
+ "move",
67
79
  "edit",
80
+ "system",
68
81
  "export",
69
82
  "upload",
70
83
  ];
71
84
  export const RUN_EXAMPLE = `const html = (tone) => \`<section data-part="hero" class="\${tone}"><h1>Writer</h1></section>\`;
72
- await mockpit.publish({ mock: "writer", variant: "calm", html: html("calm") });
73
- await mockpit.publish({ mock: "writer", variant: "bold", html: html("bold") });
85
+ await mockpit.publish({ design: "writer", variant: "calm", html: html("calm") });
86
+ await mockpit.publish({ design: "writer", variant: "bold", html: html("bold") });
74
87
  const { url } = await mockpit.ask("writer", [{ id: "tone", text: "Calm or bold?",
75
88
  options: [{ label: "Calm", variant: "calm" }, { label: "Bold", variant: "bold" }] }]);
76
89
  // Tell the user where to look and end the turn; a later run reads mockpit.feedback().
77
90
  return url;`;
78
- export const RUN_DESCRIPTION = "Run a script against mockpit, which shows design work to the user (project > mock > state > " +
91
+ export const RUN_DESCRIPTION = "Run a script against mockpit, which shows design work to the user (project > design > state > " +
79
92
  "variant > version). Publish variants and ask in one call; nothing waits for the user. Returns " +
80
93
  "value, prints, calls (every host call in order: which writes landed), error {kind, message, " +
81
94
  "line} and feedback (every batch received, even if the script failed). Read mockpit.guide() " +
@@ -1,13 +1,26 @@
1
- import { type Ask, type Asset, type Comment, type CommentQuery, type CommitReplyInput, type CreateAssetInput, type CreateCommentInput, type CreateMockInput, type CreatePostInput, type CreateSessionInput, type Draft, type Knobs, type Mock, type Post, type PostQuery, type PostStatus, type PostVersion, type Session, type Embed, type Flow, type FlowStep, type ForkRef, type SqlStorage, type Store, type Surface, type UpdateMockInput, type UpdatePostInput } from "./types.ts";
1
+ import { type Ask, type JumpRows, type DesignKind, type Asset, type Comment, type CommentQuery, type CommitReplyInput, type CreateAssetInput, type CreateCommentInput, type CreateDesignInput, type CreatePostInput, type CreateSessionInput, type Draft, type Knobs, type Design, type DesignNotes, type AnswerAddition, type AnswerSet, type AnswerStanding, type StateOutline, type Post, type CopyRef, type LiveCopy, type PostHead, type PostQuery, type PostStatus, type PostVersion, type Reply, type Session, type Embed, type Flow, type FlowStep, type ForkRef, type SqlStorage, type Store, type Surface, type UpdateDesignInput, type UpdatePostInput, type AmendPostInput, type DecisionVariant } from "./types.ts";
2
+ import { type System } from "./system.ts";
3
+ import type { Mode } from "./themes.ts";
2
4
  export declare class SqlStore implements Store {
3
5
  private sql;
4
6
  private assetRefCache;
5
- constructor(sql: SqlStorage);
7
+ private postCache;
8
+ private postCacheBytes;
9
+ private postCacheLimit;
10
+ private raw;
11
+ private holdPostDrop;
12
+ constructor(sql: SqlStorage, opts?: {
13
+ postCacheBytes?: number;
14
+ });
6
15
  private atomic;
7
16
  private tables;
8
17
  private columns;
9
18
  private addMissing;
19
+ private migrateToDesignWords;
20
+ private migrateAskScopeWord;
21
+ private renameMocksToDesigns;
10
22
  private migrate;
23
+ private migrateSituations;
11
24
  private migrateToContractTerms;
12
25
  private createIndexes;
13
26
  private migrateLegacy;
@@ -16,7 +29,7 @@ export declare class SqlStore implements Store {
16
29
  private migrateSurfaceIds;
17
30
  private migrateToItems;
18
31
  private migrateToVersions;
19
- private migrateToMocks;
32
+ private migrateToDesigns;
20
33
  private migrateToSketches;
21
34
  private settingSync;
22
35
  private insertVersion;
@@ -24,7 +37,7 @@ export declare class SqlStore implements Store {
24
37
  private historiesFor;
25
38
  private rowsToPosts;
26
39
  private rowToSession;
27
- private rowToMock;
40
+ private rowToDesign;
28
41
  private rowToPost;
29
42
  private rowToAsset;
30
43
  private rowToComment;
@@ -41,165 +54,197 @@ export declare class SqlStore implements Store {
41
54
  getSetting(key: string): Promise<string | null>;
42
55
  setSetting(key: string, value: string): Promise<void>;
43
56
  listProjects(): Promise<import("./types.ts").ProjectSummary[]>;
44
- listMocks(project?: string): Promise<Mock[]>;
45
- getMock(id: string): Promise<Mock | null>;
46
- findMock(project: string, slug: string): Promise<Mock | null>;
47
- createMock(input: CreateMockInput): Promise<Mock>;
48
- updateMock(id: string, patch: UpdateMockInput): Promise<Mock | null>;
49
- removeMock(id: string): Promise<boolean>;
50
- renameMock(id: string, slug: string): Promise<Mock | null>;
57
+ listJumpRows(): Promise<JumpRows>;
58
+ listDesigns(project?: string): Promise<Design[]>;
59
+ getDesign(id: string): Promise<Design | null>;
60
+ findDesign(project: string, slug: string): Promise<Design | null>;
61
+ createDesign(input: CreateDesignInput): Promise<Design>;
62
+ updateDesign(id: string, patch: UpdateDesignInput): Promise<Design | null>;
63
+ removeDesign(id: string): Promise<boolean>;
64
+ nameFirstState(id: string, state: string): Promise<Design | null>;
65
+ renameDesign(id: string, slug: string): Promise<Design | null>;
51
66
  renameProject(from: string, to: string): Promise<boolean>;
67
+ getSystem(project: string): Promise<System | null>;
68
+ putSystem(system: System, meta: {
69
+ sessionId: string | null;
70
+ summary: string;
71
+ }): Promise<System>;
72
+ commitSystemDecision(input: {
73
+ system: System;
74
+ summary: string;
75
+ tell: {
76
+ sessionId: string;
77
+ payload: Reply;
78
+ } | null;
79
+ }): Promise<Comment | null>;
80
+ private writeSystem;
81
+ listSystemRevisions(project: string): Promise<{
82
+ rev: number;
83
+ at: string;
84
+ sessionId: string | null;
85
+ summary: string;
86
+ }[]>;
87
+ getSystemRevision(project: string, rev: number): Promise<System | null>;
88
+ private migrateSystems;
52
89
  private flowsOf;
53
90
  private writeFlow;
54
91
  listFlows(project: string): Promise<Flow[]>;
55
92
  placeFlowStep(project: string, name: string, screen: FlowStep, step: number): Promise<Flow | null>;
56
- putDraft(mockId: string, draft: Draft | null): Promise<{
57
- draft: Draft | null;
93
+ putDraft(designId: string, draft: Draft | null): Promise<{
58
94
  id: string;
59
95
  project: string;
60
96
  slug: string;
61
97
  title: string;
62
- kind: import("./types.ts").MockKind;
98
+ kind: DesignKind;
63
99
  area: string | null;
64
100
  source: string | null;
65
101
  states: string[];
66
- situations: Record<string, string[]>;
67
102
  asks: Ask[];
68
103
  knobs: Knobs;
69
104
  widths: number[];
70
105
  sessionId: string | null;
71
- closedAt: string | null;
106
+ shortlist: boolean;
107
+ archivedAt: string | null;
108
+ archiveReason: string | null;
109
+ notes: DesignNotes | null;
110
+ stateOutlines: Record<string, StateOutline>;
111
+ answerAdditions: AnswerAddition[];
112
+ standings: Record<string, AnswerStanding>;
113
+ sets: AnswerSet[];
72
114
  createdAt: string;
73
115
  updatedAt: string;
116
+ draft: Draft | null;
74
117
  } | null>;
75
- private touchMock;
118
+ private touchDesign;
76
119
  listPosts(query?: PostQuery): Promise<Post[]>;
120
+ listPostHeads(project?: string): Promise<PostHead[]>;
121
+ listCopies(project: string): Promise<LiveCopy[]>;
122
+ private postsOfDesign;
123
+ private trimPostCache;
124
+ private writingPosts;
125
+ private cachedPost;
126
+ private recache;
77
127
  countPostsBySession(): Promise<Map<string, number>>;
78
128
  getPost(id: string): Promise<Post | null>;
79
- findPost(mockId: string, state: string | null, variant: string): Promise<Post | null>;
129
+ findPost(designId: string, state: string | null, variant: string): Promise<Post | null>;
80
130
  createPost(input: CreatePostInput): Promise<Post | null>;
81
131
  updatePost(id: string, patch: UpdatePostInput): Promise<{
82
- author?: string | undefined;
83
- embeds: Embed[];
84
- state: string | null;
85
- version: number;
86
- updatedAt: string;
87
- history: PostVersion[];
88
- from: number;
89
- prompt: string;
90
- note?: string | undefined;
91
- knobs?: Knobs | undefined;
92
- title: string;
93
- surfaces: Surface[];
94
132
  id: string;
95
133
  sessionId: string;
96
- mock: string;
134
+ design: string;
97
135
  variant: string;
98
136
  status: PostStatus;
99
137
  createdAt: string;
100
138
  forkedFrom?: ForkRef;
101
- starred?: true;
139
+ copiedFrom?: CopyRef;
102
140
  acceptedVersion?: number;
103
141
  hiddenVersions?: number[];
104
- } | null>;
105
- removePost(id: string): Promise<boolean>;
106
- setPostStatus(id: string, status: PostStatus, acceptedVersion?: number): Promise<{
107
- status: PostStatus;
108
- id: string;
109
- sessionId: string;
110
- mock: string;
111
- state: string | null;
112
- variant: string;
142
+ archiveReason?: string;
143
+ archivedAt?: string;
144
+ stayedVersion?: number;
145
+ rewrites?: number;
146
+ contract: number;
147
+ system?: number | undefined;
113
148
  title: string;
114
149
  surfaces: Surface[];
115
- createdAt: string;
116
- updatedAt: string;
117
- version: number;
118
- history: PostVersion[];
119
- knobs?: Knobs;
120
- note?: string;
150
+ knobs?: Knobs | undefined;
151
+ description?: string | undefined;
121
152
  embeds: Embed[];
122
- forkedFrom?: ForkRef;
123
- starred?: true;
124
- hiddenVersions?: number[];
125
- from?: number;
126
- prompt?: string;
127
- author?: string;
128
- } | {
129
- status: PostStatus;
130
- acceptedVersion: number;
131
- id: string;
132
- sessionId: string;
133
- mock: string;
134
153
  state: string | null;
135
- variant: string;
136
- title: string;
137
- surfaces: Surface[];
138
- createdAt: string;
139
- updatedAt: string;
140
154
  version: number;
155
+ updatedAt: string;
141
156
  history: PostVersion[];
142
- knobs?: Knobs;
143
- note?: string;
144
- embeds: Embed[];
145
- forkedFrom?: ForkRef;
146
- starred?: true;
147
- hiddenVersions?: number[];
148
- from?: number;
149
- prompt?: string;
150
- author?: string;
157
+ from: number;
158
+ prompt: string;
159
+ author?: string | undefined;
160
+ publishedBy?: string | undefined;
151
161
  } | null>;
152
- setPostStarred(id: string, starred: boolean): Promise<{
162
+ amendPost(id: string, version: number, patch: AmendPostInput): Promise<{
153
163
  id: string;
154
164
  sessionId: string;
155
- mock: string;
165
+ design: string;
156
166
  state: string | null;
157
167
  variant: string;
158
168
  status: PostStatus;
159
- title: string;
160
- surfaces: Surface[];
161
169
  createdAt: string;
162
170
  updatedAt: string;
163
171
  version: number;
164
172
  history: PostVersion[];
165
- knobs?: Knobs;
166
- note?: string;
167
- embeds: Embed[];
168
173
  forkedFrom?: ForkRef;
174
+ copiedFrom?: CopyRef;
169
175
  acceptedVersion?: number;
170
176
  hiddenVersions?: number[];
171
- from?: number;
172
- prompt?: string;
173
- author?: string;
174
- } | {
175
- starred: true;
176
- id: string;
177
- sessionId: string;
178
- mock: string;
179
- state: string | null;
180
- variant: string;
181
- status: PostStatus;
177
+ archiveReason?: string;
178
+ archivedAt?: string;
179
+ stayedVersion?: number;
180
+ publishedBy?: string;
181
+ system?: number;
182
182
  title: string;
183
183
  surfaces: Surface[];
184
- createdAt: string;
185
- updatedAt: string;
186
- version: number;
187
- history: PostVersion[];
188
- knobs?: Knobs;
189
- note?: string;
184
+ knobs?: Knobs | undefined;
185
+ description?: string | undefined;
190
186
  embeds: Embed[];
191
- forkedFrom?: ForkRef;
192
- acceptedVersion?: number;
193
- hiddenVersions?: number[];
194
- from?: number;
195
- prompt?: string;
196
- author?: string;
187
+ from?: number | undefined;
188
+ prompt?: string | undefined;
189
+ author?: string | undefined;
190
+ amendedAt: string;
191
+ rewrites: number;
192
+ contract: number;
193
+ } | null>;
194
+ setPosterOnce(id: string, version: number, poster: string, mode?: Mode, system?: number): Promise<boolean>;
195
+ withdrawVersion(id: string, version: number, embeds: Embed[]): Promise<{
196
+ post: null;
197
+ } | {
198
+ post: {
199
+ id: string;
200
+ sessionId: string;
201
+ design: string;
202
+ state: string | null;
203
+ variant: string;
204
+ status: PostStatus;
205
+ createdAt: string;
206
+ knobs?: Knobs;
207
+ description?: string;
208
+ forkedFrom?: ForkRef;
209
+ copiedFrom?: CopyRef;
210
+ acceptedVersion?: number;
211
+ archiveReason?: string;
212
+ archivedAt?: string;
213
+ stayedVersion?: number;
214
+ title: string;
215
+ surfaces: Surface[];
216
+ embeds: Embed[];
217
+ updatedAt: string;
218
+ version: number;
219
+ history: PostVersion[];
220
+ from?: number | undefined;
221
+ prompt?: string | undefined;
222
+ author?: string | undefined;
223
+ publishedBy?: string | undefined;
224
+ amendedAt?: string | undefined;
225
+ hiddenVersions?: number[] | undefined;
226
+ contract?: number | undefined;
227
+ system?: number | undefined;
228
+ rewrites: number;
229
+ };
197
230
  } | null>;
231
+ removePost(id: string): Promise<boolean>;
232
+ setPostStatus(id: string, status: PostStatus, acceptedVersion?: number): Promise<Post | null>;
233
+ setPostArchived(id: string, archivedAt: string, reason: string | null): Promise<Post | null>;
234
+ setForkedFrom(id: string, from: ForkRef | null): Promise<Post | null>;
235
+ bumpRewrites(designId: string): Promise<void>;
198
236
  setVersionHidden(id: string, version: number, hidden: boolean): Promise<Post | null>;
237
+ deleteOldVersions(id: string, versions: number[]): Promise<Post | null>;
238
+ setPostReason(id: string, reason: string | null): Promise<Post | null>;
239
+ setPostStayed(id: string, version: number): Promise<Post | null>;
199
240
  listComments(query: CommentQuery): Promise<Comment[]>;
200
241
  private insertComment;
201
242
  createComment(input: CreateCommentInput): Promise<Comment | null>;
202
243
  removeComment(id: string): Promise<Comment | null>;
244
+ listDecisionSources(project: string): Promise<{
245
+ variants: DecisionVariant[];
246
+ decidedAt: Record<string, string>;
247
+ }>;
203
248
  commitReply(input: CommitReplyInput): Promise<Comment | null>;
204
249
  private referencedAssetIds;
205
250
  private addAssetRefs;