@clearlysh/beehaven 0.8.38 → 0.8.39

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.
@@ -0,0 +1,43 @@
1
+ /**
2
+ * MCP2-65 — what `beehaven agent login <new name>` says when the workspace already has all the
3
+ * agents its plan includes (Free includes one).
4
+ *
5
+ * The identity mints at the account; joining the workspace is where the cap refuses
6
+ * (`agent-session-start` → automatic admission → `agent-create`, code `agent-limit-reached`). The
7
+ * reason alone left the person at a wall. The three choices, in this order:
8
+ * 1. log in as an agent they already have — one agent works from several apps;
9
+ * 2. upgrade for more agents (a pricing link: this is the terminal, not an MCP tool result);
10
+ * 3. archive one in Agents settings, then log in again.
11
+ *
12
+ * Pure: returns plain lines, the command colours them.
13
+ */
14
+ export const AGENT_LIMIT_CODE = 'agent-limit-reached';
15
+ export function isAgentLimitRefusal(code) {
16
+ return String(code ?? '') === AGENT_LIMIT_CODE;
17
+ }
18
+ /** An absolute pricing link from the refusal's relative `upgradeUrl` and the web origin for this env. */
19
+ export function upgradeLink(origin, upgradeUrl) {
20
+ const base = String(origin || 'https://www.clearly.sh').replace(/\/+$/, '');
21
+ const raw = typeof upgradeUrl === 'string' ? upgradeUrl.trim() : '';
22
+ // Only a site-relative path is joined; anything else falls back to the pricing page.
23
+ const path = /^\/(?!\/)[A-Za-z0-9/_?=&.-]*$/.test(raw) ? raw : '/pricing';
24
+ return `${base}${path}`;
25
+ }
26
+ export function agentLimitLoginLines(opts) {
27
+ const agents = (Array.isArray(opts.details?.activeAgents) ? opts.details.activeAgents : [])
28
+ .map((a) => ({ id: String(a?.id ?? '').trim(), name: String(a?.name ?? '').trim() }))
29
+ .filter((a) => a.id && a.id !== opts.name);
30
+ const first = agents[0];
31
+ const listed = agents.map((a) => (a.name && a.name !== a.id ? `${a.id} ("${a.name}")` : a.id)).join(', ');
32
+ const reason = String(opts.reason || '').trim();
33
+ return {
34
+ headline: `Signed in, but ${opts.workspace || 'this workspace'} is at its agent limit${reason ? `: ${reason}` : '.'}`,
35
+ choices: [
36
+ first
37
+ ? `Use the agent you already have (one agent works from several apps): beehaven agent login ${first.id}${agents.length > 1 ? ` · also yours here: ${listed}` : ''}`
38
+ : 'Use an agent you already have (one agent works from several apps): beehaven agent login <its name>',
39
+ `Upgrade for more agents: ${upgradeLink(opts.origin, opts.details?.upgradeUrl)}`,
40
+ `Or archive one in Agents settings, then run: beehaven agent login ${opts.name}`,
41
+ ],
42
+ };
43
+ }
@@ -365,11 +365,18 @@ export function loginSessionOutcome(started, thrown) {
365
365
  return { kind: 'started', sessionId: id, brief: payload?.brief };
366
366
  const err = payload?.error ?? started?.error;
367
367
  if (err) {
368
+ const code = String(payload?.code ?? started?.code ?? '');
368
369
  return {
369
370
  kind: 'refused',
370
371
  reason: typeof err === 'string' ? err : String(err?.message ?? JSON.stringify(err)),
371
- code: String(payload?.code ?? started?.code ?? ''),
372
+ code,
372
373
  scope: String(payload?.scope ?? started?.scope ?? ''),
374
+ // MCP2-65: the agent-cap refusal names the caller's live agents and a pricing path, so the
375
+ // login can print the three choices instead of a bare reason.
376
+ ...(code === 'agent-limit-reached' ? { details: {
377
+ activeAgents: Array.isArray(payload?.activeAgents) ? payload.activeAgents : [],
378
+ ...(typeof payload?.upgradeUrl === 'string' ? { upgradeUrl: payload.upgradeUrl } : {}),
379
+ } } : {}),
373
380
  };
374
381
  }
375
382
  return { kind: 'refused', reason: 'the workspace returned no session', code: '', scope: '' };
@@ -53,9 +53,9 @@ export const PUBLIC_BUILD = true;
53
53
  * ⚠ REWRITTEN IN `dist/` AT BUILD TIME, like `PUBLIC_BUILD`. `unstamped` is what a source checkout
54
54
  * carries and what `tsx`/`vitest` see, and it is honest: nothing has stamped them.
55
55
  */
56
- export const BUILD_SHA = '6f5ce18b17c5';
56
+ export const BUILD_SHA = '3026e5a147f9';
57
57
  /** ISO-8601 minute precision, UTC. Rewritten in `dist/` by `scripts/stamp-build-channel.mjs`. */
58
- export const BUILD_TIME = '2026-10-30T07:25Z';
58
+ export const BUILD_TIME = '2026-11-12T22:52Z';
59
59
  /**
60
60
  * The build identity as one short string, for `--version`.
61
61
  *
@@ -58,14 +58,29 @@ function batchReceipt(action, r) {
58
58
  for (const s of steps)
59
59
  counts.set(verb(s?.action ?? '?'), (counts.get(verb(s?.action ?? '?')) ?? 0) + 1);
60
60
  const mix = [...counts].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([a, n]) => (n > 1 ? `${a} ×${n}` : a)).join(', ');
61
+ /**
62
+ * CDS-163 — THE BATCH'S OWN DELTA WINS. Only update steps carry a per-step `canvasChanges`, so
63
+ * summing steps printed "+0 ~69 −0 layers" for a 343-step build that created 211 layers. The worker
64
+ * returns one batch-level `canvasChanges`, diffed over every node the batch addressed (creates
65
+ * included); the per-step sum is kept for an older worker that does not send it.
66
+ */
61
67
  let added = 0, changed = 0, removed = 0, sawDelta = false;
62
- for (const s of steps) {
63
- const d = s?.value?.canvasChanges;
64
- if (d && typeof d === 'object' && !Array.isArray(d)) {
65
- sawDelta = true;
66
- added += Number(d.added || 0);
67
- changed += Number(d.changed || 0);
68
- removed += Number(d.removed || 0);
68
+ const isDelta = (d) => !!d && typeof d === 'object' && !Array.isArray(d);
69
+ if (isDelta(r.canvasChanges)) {
70
+ sawDelta = true;
71
+ added = Number(r.canvasChanges.added || 0);
72
+ changed = Number(r.canvasChanges.changed || 0);
73
+ removed = Number(r.canvasChanges.removed || 0);
74
+ }
75
+ else {
76
+ for (const s of steps) {
77
+ const d = s?.value?.canvasChanges;
78
+ if (isDelta(d)) {
79
+ sawDelta = true;
80
+ added += Number(d.added || 0);
81
+ changed += Number(d.changed || 0);
82
+ removed += Number(d.removed || 0);
83
+ }
69
84
  }
70
85
  }
71
86
  const rev = r.canvasNodesRev ?? [...steps].reverse().find((s) => s?.value?.canvasNodesRev != null)?.value?.canvasNodesRev;
package/dist/cli.js CHANGED
@@ -1881,6 +1881,7 @@ async function run() {
1881
1881
  * would be refused while `conversations --json` worked.
1882
1882
  */
1883
1883
  const JSON_CAPABLE = new Set([
1884
+ 'doc', 'search',
1884
1885
  'admin', 'apps', 'd1', 'describe', 'explain', 'marketplace', 'secrets',
1885
1886
  'shop', 'user', 'webhook', 'webhooks', 'workspaces', 'ws',
1886
1887
  // Honour what the modules already implement.
@@ -26,6 +26,7 @@ import { join, dirname } from 'node:path';
26
26
  import { homedir } from 'node:os';
27
27
  import { rpcOnce } from '../ipc-client.js';
28
28
  import { accountMintRequest, accountMintResponse } from '../agent-mint-response.js';
29
+ import { agentLimitLoginLines, isAgentLimitRefusal } from '../agent-limit-refusal.js';
29
30
  import { saveAgent, loadAgent, listAgents, deleteAgent, findAgentByAvatar, isValidAgentName, resolveActiveAgent, activeAgentName, saveAgentSession, clearAgentSession, clearAgentSessionId, decodeAgentTokenClaims, } from '../agent-state.js';
30
31
  const VALID_KINDS = ['claude-code', 'daemon-agent', 'clearly-ai'];
31
32
  export const FREE_SKINS = [
@@ -699,6 +700,12 @@ async function agentLogin(args, values) {
699
700
  }
700
701
  catch (err) {
701
702
  console.error(` Could not mint identity: ${err?.message ?? err}`);
703
+ if (String(err?.code) === 'identity-limit-reached') {
704
+ // MCP2-71: the account's safety ceiling — no plan lifts it; signing in as one you have always works.
705
+ console.error(' Sign in as an agent you already have: beehaven agent list, then beehaven agent login <name>');
706
+ console.error(' Or delete one you no longer use in Agents, then run this again.');
707
+ process.exit(1);
708
+ }
702
709
  if (!err?.code || /^(ECONN|ENOENT|ETIMEDOUT)/.test(String(err.code)))
703
710
  console.error(' Is the daemon connected? Check with: beehaven status');
704
711
  process.exit(1);
@@ -964,6 +971,21 @@ async function agentLogin(args, values) {
964
971
  console.log(` ${green('◷')} Workspace work session ${cyan(outcome.sessionId.slice(0, 8))} started.`);
965
972
  printBrief(outcome.brief);
966
973
  }
974
+ else if (outcome.kind === 'refused' && isAgentLimitRefusal(outcome.code)) {
975
+ // MCP2-65: at the plan's agent limit, say what to do — not just that it was refused.
976
+ const { resolveAuthOrigin } = await import('../browser-login.js');
977
+ const lines = agentLimitLoginLines({
978
+ name, workspace: (await workspaceLabel(chosenWorkspace)).split(/ {2}\(| — /)[0], reason: outcome.reason,
979
+ details: outcome.details, origin: resolveAuthOrigin().authOrigin,
980
+ });
981
+ console.log('');
982
+ console.error(` ${yellow('⚠')} ${lines.headline}`);
983
+ console.error(' Your choices:');
984
+ for (const line of lines.choices)
985
+ console.error(` · ${line}`);
986
+ console.error(dim(` Calls from this session run as ${name} and will be refused — they will NOT fall back to another agent.`));
987
+ process.exitCode = 1;
988
+ }
967
989
  else if (outcome.kind === 'refused') {
968
990
  console.log('');
969
991
  console.error(` ${yellow('⚠')} Signed in, but this workspace refused ${cyan(name)}: ${outcome.reason}`);
@@ -26,6 +26,8 @@ import { resolveActiveAgent } from '../agent-state.js';
26
26
  import { isAgentRuntime } from '../agent-scope.js';
27
27
  import { getTarget } from '../cli-state.js';
28
28
  import { attachShellCwd, absorbShellCwd, shellTargetKey } from '../shell-session.js';
29
+ import { planLimitAdvice } from '../limit-advice.js';
30
+ import { resolveAuthOrigin } from '../browser-login.js';
29
31
  /** Workspace-agent jobs wait for the server's ceiling (900s) plus the report's tail. */
30
32
  export const AGENT_TURN_TIMEOUT_MS = 930_000;
31
33
  const AGENT_TURN_ACTIONS = new Set(['agent-prompt', 'agent-turn']);
@@ -1287,6 +1289,16 @@ function printKnownActionAdvice(action, input, result) {
1287
1289
  if (action === 'ticket-get' && result?.code === 'not-found') {
1288
1290
  console.error(' If this key belongs to a document, read it with document-get; ticket-get reads board cards only.');
1289
1291
  }
1292
+ // MCP2-70 / MCP2-71: a plan or account limit says what is true and what to do, in words.
1293
+ if (/-limit-reached$/.test(String(result?.code ?? ''))) {
1294
+ let origin = 'https://www.clearly.sh';
1295
+ try {
1296
+ origin = resolveAuthOrigin().authOrigin;
1297
+ }
1298
+ catch { /* the default web origin */ }
1299
+ for (const line of planLimitAdvice(result, origin))
1300
+ console.error(line);
1301
+ }
1290
1302
  }
1291
1303
  /**
1292
1304
  * Emit navigation hints for common call failures. Agents parse these to
@@ -2463,8 +2475,8 @@ export async function cmdTask(title, values) {
2463
2475
  }
2464
2476
  // ── beehaven doc add <file> — local file → searchable project document ───
2465
2477
  //
2466
- // One-shot: reads a local file, mints a note (note-create) + seeds its body
2467
- // (note-content-seed). Content lands in the workspace SQLite note-content
2478
+ // One-shot: reads a local file and creates it through document-create.
2479
+ // Content lands in the workspace SQLite note-content
2468
2480
  // table + FTS5 index, so `search-notes` and the cloud agent can retrieve it.
2469
2481
  // This is the DOCUMENT path; `teleport` is the raw-blob (R2) path. Markdown is
2470
2482
  // stored verbatim; code is wrapped in a fenced block (with the language) so it
@@ -2525,16 +2537,14 @@ export async function cmdDocAdd(filePath, values) {
2525
2537
  const title = values.title ||
2526
2538
  (isMarkdown ? raw.match(/^#\s+(.+)$/m)?.[1]?.trim() || fileName : fileName);
2527
2539
  try {
2528
- const created = await rpcOnce('note-create', {
2529
- noteData: { projectId: values.project || undefined, title },
2530
- fileType: 'document',
2540
+ const created = await rpcOnce('document-create', {
2541
+ projectId: values.project || undefined, title, markdown: data,
2531
2542
  }, getTimeout(values));
2532
- const noteId = created?.noteId || created?.id;
2543
+ assertNoRpcError(created, 'document-create');
2544
+ const noteId = created?.id;
2533
2545
  if (!noteId) {
2534
- console.error(` note-create failed: ${JSON.stringify(created)}`);
2535
- process.exit(1);
2546
+ throw new Error(`document-create failed: ${JSON.stringify(created)}`);
2536
2547
  }
2537
- await rpcOnce('note-content-seed', { noteId, data, title }, getTimeout(values));
2538
2548
  if (values.json) {
2539
2549
  console.log(JSON.stringify({
2540
2550
  noteId,
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Plain-language advice printed under a plan or account limit refusal from `beehaven call`
3
+ * (MCP2-70 workspaces, MCP2-71 identities, MCP2-65 per-workspace agents). The refusal JSON is
4
+ * printed first; these lines say what is true and the one thing to do next. Pure: returns lines.
5
+ */
6
+ import { upgradeLink } from './agent-limit-refusal.js';
7
+ export function planLimitAdvice(result, origin) {
8
+ const code = String(result?.code ?? '');
9
+ if (code === 'workspace-limit-reached') {
10
+ const owned = Number(result?.current) || 1;
11
+ return [
12
+ owned > 1
13
+ ? ` You own ${owned} workspaces and Free includes 1. Nothing has been removed.`
14
+ : ' Free includes 1 workspace you own, and you have it. Workspaces you were invited to don\'t count.',
15
+ ` Upgrade your workspace to create more: ${upgradeLink(origin, result?.upgradeUrl)}`,
16
+ ];
17
+ }
18
+ if (code === 'identity-limit-reached') {
19
+ return [
20
+ ` Your account has ${Number(result?.current) || 'the most'} agents, the most one account can have (the same on every plan).`,
21
+ ' Delete one you no longer use in Agents, or sign in as one you have: beehaven agent list',
22
+ ];
23
+ }
24
+ if (code === 'agent-limit-reached') {
25
+ return [
26
+ ' This workspace has all the agents its plan includes. One agent can work from several apps.',
27
+ ' Use one you have (beehaven agent login <name>), archive one in Agents settings,',
28
+ ` or upgrade for more agents: ${upgradeLink(origin, result?.upgradeUrl)}`,
29
+ ];
30
+ }
31
+ return [];
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clearlysh/beehaven",
3
- "version": "0.8.38",
3
+ "version": "0.8.39",
4
4
  "description": "Beehaven CLI — command your Clearly workspace, agents, and canvas from the terminal",
5
5
  "private": false,
6
6
  "type": "module",
@@ -47,12 +47,24 @@ const SOURCES = {
47
47
  'clearly-init': 'apps/mcp-server/plugin/skills/clearly-init/SKILL.md',
48
48
  'clearly-workflows': 'apps/mcp-server/plugin/skills/clearly-workflows/SKILL.md',
49
49
  'clearly-artifacts': 'apps/mcp-server/plugin/skills/clearly-artifacts/SKILL.md',
50
+ // CC2-245 — print and social design, so the CLI teaches the same set as the app.
51
+ 'print-production': 'apps/mcp-server/plugin/skills/print-production/SKILL.md',
52
+ 'social-posts': 'apps/mcp-server/plugin/skills/social-posts/SKILL.md',
50
53
  };
51
54
 
52
55
  let missing = 0;
53
56
  const out = join(PKG, 'skills');
54
57
  mkdirSync(out, { recursive: true });
55
58
  for (const [name, rel] of Object.entries(SOURCES)) {
59
+ // Standalone package CI has no Clearly monorepo. Its explicitly merged bundle is the input;
60
+ // still validate every shipped skill and regenerate the same content-hashed manifest below.
61
+ if (process.env.BEEHAVEN_SKILLS_SNAPSHOT === '1') {
62
+ if (!existsSync(join(out, name, 'SKILL.md'))) {
63
+ console.error(` ✗ ${name}: packaged snapshot missing`);
64
+ missing++;
65
+ }
66
+ continue;
67
+ }
56
68
  const src = join(REPO, rel);
57
69
  if (!existsSync(src)) {
58
70
  /**
@@ -190,11 +190,23 @@ Anything you create is stamped `created_by: agent:<your-id>` and born **private*
190
190
  - **a SIBLING agent does not**, unless it is shared;
191
191
  - **you can see and manage your own**, and you can see your owner's work.
192
192
 
193
- ⚠⚠ **FILING INTO A SHARED PROJECT IS THE ONLY SHARING MECHANISM.** There is no per-item ACL. If a
193
+ ⚠⚠ **FILING INTO A SHARED PROJECT IS HOW YOU SHARE.** There is no per-item ACL. If a
194
194
  teammate or another agent needs your work, move it into a project you both belong to
195
195
  (`project-member-add` takes an `agentId`). A project is **open** (all workspace members) or
196
196
  **invite-only**; the workspace default is set once in Settings → Workspace → Project access.
197
197
 
198
+ Change it on something you made (document, sheet, deck or canvas) with one call, CLI or MCP:
199
+
200
+ ```bash
201
+ beehaven call sharing-set '{"kind":"document","id":"PLA-5420","project":"Agent Work OS — Seed Roadmap"}'
202
+ beehaven call sharing-set '{"kind":"document","id":"PLA-5420","visibility":"workspace"}' # unfiled, everyone
203
+ beehaven call sharing-set '{"kind":"composition","id":"<canvas id>","project":null}' # back to Drafts, private
204
+ ```
205
+
206
+ Over MCP: `clearly_edit { target, changes: { sharing: { project?, visibility? } } }`. The reply says
207
+ who can see it now. ⚠ Only on your own work: another agent's (or your owner's) is refused `not-yours`.
208
+ Don't copy a document to share it; the copy splits its history and comments.
209
+
198
210
  ⚠ Creating something does **not** put it in your owner's Recents — that is deliberate. They find it
199
211
  in Drafts. Opening it is what makes it recent *for them*.
200
212
 
@@ -5,7 +5,7 @@ description: >-
5
5
  Put a self-contained interactive HTML page into a Clearly workspace, the way a
6
6
  Claude artifact works: an explainer, an animation, a small tool, a calculator, a
7
7
  chart that runs. Covers which of the three HTML homes runs scripts (a SITE does,
8
- a canvas ARTIFACT card does but cannot be clicked, a NOTE never does), the exact
8
+ a canvas ARTIFACT card does and is clicked only in Interact mode, a NOTE never does), the exact
9
9
  CLI calls, the sandbox limits, updating, filing in a project, how a person opens
10
10
  it, and QA. Use when asked to publish, host or add an HTML page, demo or animation
11
11
  to the workspace. Triggers: "make an artifact", "publish this HTML", "add this page /
@@ -23,13 +23,14 @@ production hold different workspaces.
23
23
  | Home | Make it with | Scripts and CSS animation | Clickable | Search, history | Opens at |
24
24
  |---|---|---|---|---|---|
25
25
  | **Site** | `compose-upload-init` → `file-teleport-inline` → `compose-upload-finalize` | run | yes | not in `document-search`; no version history; a re-upload overwrites | Recents or project card → `/compositions/detail?id=<id>` |
26
- | **Canvas artifact** | `canvas-act` `canvas.add-artifact` | run | **no**: the card never takes pointer events, so it pans like a picture | no; it is part of the canvas and is deleted with it | the canvas, `/c/<compositionId>` |
26
+ | **Canvas artifact** | `canvas-act` `canvas.add-artifact` | run | **only in Interact mode**: by default the card takes no pointer events and pans like a picture; the person selects it and presses **Interact** (Done or Esc hands input back to the canvas) | no; it is part of the canvas and is deleted with it | the canvas, `/c/<compositionId>` |
27
27
  | **Note** | `document-create {html}`; `page-write` (in-app chat only) | **never**: it opens in the document editor, which drops `<script>`, `<style>` and inline `<svg>` | n/a | yes, both | `/documents/<id>` |
28
28
 
29
29
  - An interactive page someone opens, clicks through or comes back to is a **site**. This is the
30
30
  Claude-artifact equivalent and the default.
31
31
  - A page that only needs to be **seen** beside other cards (a looping animation, a live chart in a
32
- review board) is a **canvas artifact**. Buttons inside it cannot be pressed.
32
+ review board) is a **canvas artifact**. Its buttons respond only after the person selects the
33
+ card and presses Interact, so never make a control the only way to see the content.
33
34
  - Prose the team will read, search and edit is a **note**. Never put a script in a note: it
34
35
  is stored, and then nothing ever runs it. Editing the note in the app rewrites its body.
35
36
 
@@ -99,8 +100,8 @@ artifact card. It copies the entry HTML at that moment: later re-uploads do not
99
100
  and the site's other files do not load.
100
101
 
101
102
  **Connector only (MCP):** no tool makes a site or an artifact. `clearly_file_upload` cannot choose
102
- a site path, and `clearly_create` stores a document body as markdown. `clearly_canvas_place
103
- {target, kind:"site", id}` can place a site that already exists. Otherwise, tell the person the
103
+ a site path, and `clearly_create` stores a document body as markdown. `clearly_canvas_add
104
+ {target, ops:[{op:"place", kind:"site", id}]}` can place a site that already exists. Otherwise, tell the person the
104
105
  running page needs the CLI.
105
106
 
106
107
  ## 4. Update, file, link
@@ -2,8 +2,9 @@
2
2
  name: clearly-docs
3
3
  description: >-
4
4
  Read, search, edit and recover a company's documents in a Clearly workspace.
5
- Use the seven-tool MCP filesystem for ordinary work; use the `beehaven` CLI
6
- only for document history, diffs, and restore. Load this whenever the answer
5
+ Use the MCP filesystem tools (clearly_glob, clearly_grep, clearly_read,
6
+ clearly_edit…) for ordinary work; use the `beehaven` CLI only for document
7
+ history, diffs, and restore. Load this whenever the answer
7
8
  depends on what the company has written down, or when you are about to write
8
9
  something down for it. Triggers: "what do our docs say", "find the doc about
9
10
  X", "update the spec", "write this up", "add it to the docs", "has this
@@ -101,6 +102,42 @@ whatever you last read, overwriting anything the human changed in between.
101
102
  a whole existing document, re-read it immediately before writing and verify the returned body.
102
103
  Prefer `clearly_edit` whenever a precise passage change can express the job.
103
104
 
105
+ ## Formatting a document (PRDs, specs, memos)
106
+
107
+ A document renders like a Google Doc: Arial 11pt, 1.15 line spacing, Google's heading sizes, and no space
108
+ between blocks except what you write. Write the body in `markdown`; switch to `html` only for what markdown
109
+ cannot say.
110
+
111
+ **Markdown**
112
+ - `<!-- clearly-style:title -->` on the line above `# Name` makes the Title; `<!-- clearly-style:subtitle -->`
113
+ above `## Line` the Subtitle. `#` to `######` are Headings 1-6. Numbered headings ("1. Summary") are spaced
114
+ exactly like plain ones.
115
+ - A blank line between two paragraphs, lists, tables, quotes or code blocks is an empty line on the page —
116
+ put one wherever you would press Enter twice in Google Docs. None is needed next to a heading.
117
+ - `1.` lists keep their numbers: a list that resumes at `3.` after a paragraph continues at 3. Indent 2-4
118
+ spaces to nest (1. a. i. and bullets ● ○ ■). `- [ ]` / `- [x]` is a checklist.
119
+ - GFM tables: the first row is the header; `:---:` centres a column, `---:` right-aligns it.
120
+ - `**bold**`, `*italic*`, `~~strike~~`, `==highlight==`, `` `code` ``, `[text](https://…)`, and inline
121
+ `<u>`, `<sub>`, `<sup>`, `<br>`, `<span style="color: #dc2626">…</span>`,
122
+ `<span style="background-color: #fef08a">…</span>`. Any other HTML in markdown shows as text.
123
+ - `> text` is a quote; `> [!NOTE]` (TIP, IMPORTANT, WARNING, CAUTION) opens a callout with that bold label.
124
+ - Fenced code (```` ```ts ````), `---` for a horizontal line, `[TOC]` for a table of contents (filled from
125
+ Headings 1-3 when the document opens), `<!-- pagebreak -->` for a page break, and
126
+ `<!-- clearly-align:center -->` (left, right, justify) on the line above a paragraph or heading to align it.
127
+
128
+ **HTML** (send `html` instead of `markdown`) reaches every option the editor has:
129
+ `<span style="font-size: 14pt">`, `<span style="font-family: Georgia">`, `<p style="padding-top: 10pt;
130
+ padding-bottom: 10pt">` (space before/after), `<p data-line-spacing="1.5">`, `<p style="margin-left: 36pt">`
131
+ (indent), `<p style="text-align: justify">`, `<mark data-color="#bfdbfe" style="background-color: #bfdbfe">`,
132
+ `<th colwidth="120">` (column width), `<ul data-type="taskList"><li data-type="taskItem" data-checked="true"><p>…</p></li></ul>`,
133
+ `<div data-toc></div>`, `<div data-type="page-break"></div>`, `<h1 class="doc-title">`, `<h2 class="doc-subtitle">`.
134
+ An empty line is `<p></p>`.
135
+
136
+ **A PRD, the way big-tech teams write one:** Title, then a Subtitle with team · status · version; a
137
+ two-column table of Owner, Status, Reviewers and Target launch; `[TOC]`; then Summary, Problem, Goals and
138
+ Non-goals (a checklist), Requirements (a table of ID, requirement, priority, notes), User experience, Metrics
139
+ (a table of baseline and target), Launch plan (a numbered list), Risks and open questions (callouts), Appendix.
140
+
104
141
  ## Comments and @mentions — say it beside the work, not instead of it
105
142
 
106
143
  A comment is ABOUT something; an edit changes it. When you have a question or a note for a person,
@@ -67,24 +67,33 @@ into a document, ticket, canvas, log, or chat response.
67
67
  ### 2. Verify
68
68
 
69
69
  Run `/mcp` in Claude Code, or `codex mcp list` in Codex. You should see `clearly` authenticated
70
- with **7 tools**:
70
+ with its tools listed: `clearly_read`, `clearly_create` and `clearly_canvas_add` among them. A
71
+ client that connected before 2026-10-30 may show its older list until it reconnects; the old names
72
+ keep working.
71
73
 
72
- **Seven MCP tools, split by what they DO rather than by what they act on.** The four CRUD tools
73
- cover every artifact type — document, canvas, sheet, deck, board, ticket, project — so the tool fixes
74
- whether it reads or writes and `type` says what it touches.
74
+ **Every tool has one safety class: its name says whether it reads, adds, edits or deletes.** The
75
+ CRUD tools cover every artifact type — document, canvas, sheet, deck, board, ticket, project — and
76
+ `type` says what they touch.
75
77
 
76
78
  - **`clearly_catalog`** — optional, lazy machine-readable schemas. Use `{ type, operation }` before a type-specific write/edit; omit both for the compact capability overview.
77
79
  - **`clearly_read` / `clearly_create` / `clearly_edit` / `clearly_replace` / `clearly_delete`** — open one, make a new one, change part of one, overwrite one's whole body, archive one. Each takes a uniform same-operation `batch`.
78
80
  - **`clearly_comment`** — comment on any item (with @mentions), or send and reply to messages.
79
81
  - **`clearly_grep` / `clearly_glob`** — find by content, find by name. Both span notes and code.
82
+ - **Canvas tools** — `clearly_canvas_read` (layers, or a `view`: inspect, measure, text, fonts,
83
+ vectors, prototype, variables, live, table), `_add` (nodes and additive ops), `_update` (edits and
84
+ ops: auto layout, variables, prototype links, vector edits…), `_delete`, `_review` (a saved PNG)
85
+ and `_export`. One call is one batch and one revision.
86
+ - **Workspace tools** — components, design start/plan, context and semantic search, skills,
87
+ version history and files.
80
88
 
81
89
  Addressed by `target`: a path (`~/Q1/Plan.md`), a key (`CLR-42`) or an id. Folders are projects,
82
90
  documents are `.md`, canvases are `.scene.json` — the extension names the type, and you pass `type`
83
91
  when it does not.
84
92
 
85
- **Everything else is the `beehaven` CLI**, by action name — the canvas, the Company Brain, skills,
86
- scheduling, stickers, billing. `beehaven actions` lists them; `beehaven call <action> '<json>'` runs
87
- one:
93
+ **Everything else is the `beehaven` CLI**, by action name — Company Brain writes, scheduling,
94
+ stickers and other AI media, billing. In a shell the CLI is also the faster canvas door, because one
95
+ `canvas-act` batch can mix reads and writes. `beehaven actions` lists them; `beehaven call <action>
96
+ '<json>'` runs one:
88
97
 
89
98
  ```bash
90
99
  beehaven call context-search '{"query":"per-seat billing","scope":"org"}'
@@ -103,9 +112,9 @@ Claude Code already lives and where every call is attributed to your agent.
103
112
 
104
113
  ## Install the CLI — the other half of the surface
105
114
 
106
- The seven MCP tools cover reading, writing and searching artifacts. **Everything else — the canvas,
107
- the Company Brain, skills, stickers — is reached with `beehaven`**, and the skills in this plugin
108
- assume it is on PATH. Claude Code has a shell, so this is one command:
115
+ The MCP tools cover artifacts, the canvas, components, search, skills and files. **The rest — the
116
+ Company Brain's writes, stickers and other AI media, scheduling — is reached with `beehaven`**, and
117
+ the skills in this plugin assume it is on PATH. Claude Code has a shell, so this is one command:
109
118
 
110
119
  ```bash
111
120
  curl -fsSL https://clearly.sh/install.sh | sh
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: clearly-workflows
3
3
  description: >-
4
- Best with the beehaven CLI (semantic search and write-back); with only the Clearly connector, search with clearly_grep and record decisions with clearly_create.
4
+ Best with the beehaven CLI (semantic search and write-back); with only the Clearly connector, search with clearly_search_semantic or clearly_grep and record decisions with clearly_create.
5
5
  Use the Clearly workspace as a COMPANY BRAIN — search the org's accumulated
6
6
  context (decisions, facts, prompts, docs), write conclusions back so the next
7
7
  agent inherits them instead of re-deriving them, and inherit the team's
@@ -63,7 +63,7 @@ beehaven call context-write '{
63
63
 
64
64
  It creates the document AND files a searchable catalog entry, so the very next `context-search` finds it. Mark anything sensitive `private: true`.
65
65
 
66
- ⚠ **Why these are CLI calls and not MCP tools.** The MCP surface is seven tools split by what they do to an artifact (`clearly_catalog · _read · _write · _edit · _delete · _grep · _glob`); the Company Brain's verbs are workspace ACTIONS, reached by name through the shell. Once a document exists, `clearly_grep` and `clearly_read` open it like any other.
66
+ ⚠ **Why these are CLI calls.** The MCP connector covers artifacts (`clearly_catalog · _read · _create · _edit · _replace · _comment · _delete · _grep · _glob`), the canvas and context search (`clearly_search_context`), but not the Company Brain's write verbs: those are workspace ACTIONS, reached by name through the shell. Once a document exists, `clearly_grep` and `clearly_read` open it like any other.
67
67
 
68
68
  ## 4. Inherit the team's skills
69
69
 
@@ -76,7 +76,7 @@ Follow the loaded procedure rather than inventing your own — that's how you wo
76
76
 
77
77
  ## Anything else
78
78
 
79
- `beehaven actions` lists every workspace action with its description; `beehaven call <action> '<json>'` runs one. Reach for these when the seven MCP tools don't cover what you need — which is most things beyond reading, writing and searching artifacts.
79
+ `beehaven actions` lists every workspace action with its description; `beehaven call <action> '<json>'` runs one. Reach for these when the MCP tools don't cover what you need — which is most things beyond reading, writing and searching artifacts.
80
80
 
81
81
  ## Rules of thumb
82
82
 
package/skills/index.json CHANGED
@@ -82,14 +82,14 @@
82
82
  "files": [
83
83
  "SKILL.md"
84
84
  ],
85
- "sha": "11505106cb1dd287"
85
+ "sha": "73755afe97270a0a"
86
86
  },
87
87
  {
88
88
  "name": "visualize",
89
89
  "files": [
90
90
  "SKILL.md"
91
91
  ],
92
- "sha": "88bb6e94fb37c337"
92
+ "sha": "634baf88dd3d0b2a"
93
93
  },
94
94
  {
95
95
  "name": "sticker-pack",
@@ -103,27 +103,42 @@
103
103
  "files": [
104
104
  "SKILL.md"
105
105
  ],
106
- "sha": "388be4de86f59eb4"
106
+ "sha": "044d6b33c1b07f10"
107
107
  },
108
108
  {
109
109
  "name": "clearly-init",
110
110
  "files": [
111
111
  "SKILL.md"
112
112
  ],
113
- "sha": "d4dfdd76c7c0a108"
113
+ "sha": "c3ebdc077b05bc7d"
114
114
  },
115
115
  {
116
116
  "name": "clearly-workflows",
117
117
  "files": [
118
118
  "SKILL.md"
119
119
  ],
120
- "sha": "b9622f21173bab0d"
120
+ "sha": "13aed38f4d4dfe67"
121
121
  },
122
122
  {
123
123
  "name": "clearly-artifacts",
124
124
  "files": [
125
125
  "SKILL.md"
126
126
  ],
127
- "sha": "14434d4904da91a3"
127
+ "sha": "13b9ab880f975914"
128
+ },
129
+ {
130
+ "name": "print-production",
131
+ "files": [
132
+ "SKILL.md",
133
+ "formats.md"
134
+ ],
135
+ "sha": "d436b3a56ff65b5d"
136
+ },
137
+ {
138
+ "name": "social-posts",
139
+ "files": [
140
+ "SKILL.md"
141
+ ],
142
+ "sha": "f8b095de39645000"
128
143
  }
129
144
  ]
@@ -0,0 +1,96 @@
1
+ ---
2
+ name: print-production
3
+ description: >-
4
+ Print-ready flyers, posters, leaflets and tri-folds, booklets, menus, postcards, business cards, books,
5
+ stickers and signs on a Clearly canvas: physical print pages at 96 units per inch, layout grids in mm and pt,
6
+ bleed, trim and safe areas, type for the reading distance, image resolution, CMYK risks, preflight with the
7
+ layers to fix, and press PDF export with crop marks. Load it BEFORE building anything that will be printed.
8
+ Triggers: "print-ready", "flyer", "poster", "brochure", "tri-fold", "business card", "menu", "booklet",
9
+ "zine", "send to the printer", "with bleed", "crop marks", "A4", "letter size". Needs a terminal with the
10
+ beehaven CLI; in a chat with only the Clearly connector, use the clearly_* tools (add op "artboard", update
11
+ ops "grid" and "print", read view "preflight", clearly_canvas_export).
12
+ ---
13
+
14
+ > **Canvas work goes through the CLI.** Every verb below is a `canvas-act` action:
15
+ > `beehaven call canvas-act '{"compositionId":"<id>","action":"canvas.add-artboard","args":{"preset":"a4","grid":{"margin":15,"columns":6,"gutter":5,"baseline":13}}}'`,
16
+ > or several at once with `{"compositionId":"<id>","batch":[{"action":…,"args":{…}}, …]}`. With only the Clearly
17
+ > connector: `clearly_canvas_add { ops:[{ op:"artboard", preset:"a4", grid:{…} }] }`, `clearly_canvas_update { ops:[{ op:"grid", id, … }, { op:"print", bleedMm:3, cropMarks:true }] }`,
18
+ > `clearly_canvas_read { view:"preflight", id }` and `clearly_canvas_export { ids:[page], format:"pdf" }`.
19
+ > Clearly runs no AI model for a connected agent (MCP2-57): no image generation, editing or upscaling here — place
20
+ > photos the person supplies. The in-app agent's `print-production` house skill (apps/cloudflare/src/agent/media-skills.ts)
21
+ > shares this text except for its AI image steps. Every format id, its trim, units and defaults: [formats.md](formats.md).
22
+
23
+ # print-production
24
+
25
+ Print production on the Clearly canvas: flyers, posters, leaflets and brochures, menus, postcards, business cards, books, stickers and signs that print at the right size and survive the press.
26
+
27
+ ONE SCALE
28
+ Print pages are physical: 96 canvas units per inch. 1 mm = 3.78 units and 1 pt = 1.333 units, so 10 pt type is fontSize 13.33 and a 0.5 pt rule is strokeWidth 0.67. DPI is output only: an A4 page is 793.7×1122.5 units whether it exports at 150 or 300 dpi. Never build a print page at "300 dpi pixels" or 2×.
29
+
30
+ 1 SET UP THE PAGE (one call)
31
+ canvas.add-artboard { preset, orientation?, pages?, spread?, fold?, grid? } makes print pages; frame.create with a print preset makes the same page. Presets: "a4" 210×297 mm, "a5", "a3", "a2", "a1", "letter" 8.5×11 in (also "flyer"), "tabloid" 11×17 in, "poster-18x24" 18×24 in, "poster-24x36", "poster-50x70", "postcard" 6×4 in, "postcard-5x7", "business-card" 3.5×2 in, "business-card-eu" 85×55 mm, "dl" 99×210 mm, "trifold-letter", "trifold-a4", "bifold-a4", "greeting-card", "book-6x9", "book-5x8", "sticker3", "roll-up"; any size with wMm/hMm. It saves the format's bleed and safe area as the print settings when the canvas has none, and returns the page ids, fold panels, grid numbers and output pixels.
32
+ - Booklets: pages in multiples of 4; spread:true lays page 1 as a recto, then verso|recto pairs, and the inside margins mirror.
33
+ - Folded leaflets ("half", "roll", "z", "gate" folds) come as two pages, Outside and Inside, with a column band per panel and fold guides. Roll folds are unequal: the inside flap is 2 mm narrower. Letter roll fold: Outside [inside flap | back | cover], Inside [2 | 3 | 4].
34
+
35
+ 2 GRID BEFORE CONTENT
36
+ canvas.layout-grid { id, margin, columns, gutter, baseline, side? } in mm, baseline in pt. It returns frame-relative columns [{x,w}], spans (the width of n columns) and baseline { first, step }; place on those numbers, never estimates. Starting points:
37
+ - A4/Letter flyer or menu: margins 12–15 mm, 6 or 12 columns, 4–5 mm gutter, baseline = body leading (13 pt for 10/13).
38
+ - Report or book page: inside 18–22 mm, outside 14–16, top 15, foot 20–24 (the foot is largest); 1–2 columns of 45–75 characters.
39
+ - Poster: margins 4–6% of the short side, 4–6 columns, and one element breaks the grid on purpose.
40
+ - Business card: 4–5 mm all round. Tri-fold: 6 mm inside each panel.
41
+
42
+ 3 TYPE FOR THE DISTANCE
43
+ Body 9–11 pt (fontSize 12–14.7) at 125–145% leading; captions 7–8 pt; legal small print 6 pt minimum; nothing under 4 pt. A headline needs about 32 pt per metre of reading distance (an A3 poster read at 2 m: 64–120 pt; a roll-up's key line read at 5 m: 160 pt+). Reversed type (light on dark): 8 pt minimum and a heavier weight. Small black text in pure #000000 so it prints from black ink alone. Measure with canvas.measure-text before placing; put text tops where the baselines land on the baseline grid.
44
+
45
+ 4 BLEED, TRIM, SAFE
46
+ The trim is the page edge. Anything that touches it (backgrounds, photos, colour bands) runs past it by the bleed: 3 mm = 11.34 units, 2 mm on stickers. Print pages are unclipped so bleed art shows. Keep text and logos inside the safe area (usually 5 mm, 3 mm on cards) or the grid margins. No thin borders near the trim: cutting drifts by about 1 mm.
47
+
48
+ 5 IMAGES AND COLOUR
49
+ A photo prints sharp with at least 300 source pixels per printed inch (150 for posters seen from 2 m or more); preflight reads stored source sizes. When a photo is small or soft, ask the person for a larger source image before export. Canvas colour is RGB and the printer converts to CMYK: very vivid blues, greens, violets and oranges print duller, so choose a slightly deeper colour or warn. Large black areas: ask for the printer's rich black (often C60 M40 Y40 K100); small text stays pure black. Ask for the printer's spec, spot colours and paper; they override these defaults.
50
+
51
+ 6 CHECK AND DELIVER
52
+ - canvas.review the page and open its image; fix overflow, collisions and contrast.
53
+ - canvas.preflight { id }: fix every warning on the ids it names (bleed-short, safe-area, hairline, reversed-small-text, tiny-text, low-res, vivid-rgb), then run it again until the verdict is "pass" or each remaining warning is a choice you state.
54
+ - canvas.set-print { dpi, bleedMm, safeMm, cropMarks:true } when the printer asks for marks.
55
+ - canvas.export { format:"pdf", ids:[page] }: a press PDF with trim and bleed boxes, crop marks and art at the DPI (the receipt states the DPI achieved). format:"svg" is vector with outlined text; "png" is trim plus bleed at the DPI.
56
+ - In your reply state the trim size, bleed, safe area, marks, DPI, paper if known and any colour risk.
57
+
58
+ ## Folds and panels
59
+
60
+ | fold | panels (flat → folded) | panel widths | outside order | inside order |
61
+ |---|---|---|---|---|
62
+ | half (bi-fold) | 2 | equal | back · front | 2 · 3 |
63
+ | roll (tri-fold) | 3 | outer two equal, the inside flap 2 mm narrower | inside flap · back · cover | 2 · 3 · 4 |
64
+ | z (concertina) | 3 | equal | 1 · 2 · 3 | 4 · 5 · 6 |
65
+ | gate | 3 | flaps 1 mm narrower than a quarter each, centre half | flap · back · flap | flap · centre spread · flap |
66
+
67
+ - `canvas.add-artboard { preset:"trifold-letter" }` (or `fold:"roll"` on any size) returns `fold.panelsMm`, `fold.foldsMm` and
68
+ a grid with one column band per panel. Never put type across a fold; keep 6 mm clear either side of it.
69
+ - Design the cover panel first, then the inside spread as one composition across panels 2–4.
70
+ - Heavier stock (over about 170 gsm) needs scoring before folding: say so in the handoff.
71
+
72
+ ## Booklets and imposition
73
+
74
+ - Saddle-stitched booklets are a multiple of 4 pages: 8, 12, 16, 20, 24. Design reader spreads with
75
+ `pages:N, spread:true`; the printer imposes printer spreads. Do not impose yourself unless asked.
76
+ - Inside margins grow with page count on saddle stitch (creep): add 1–2 mm to the inside margin at 24+ pages.
77
+ - Perfect-bound books lose 3–6 mm into the spine on the inside: inside margin 18–22 mm minimum.
78
+ - Page 1 is a right-hand page (recto). Odd pages are rectos; running heads and folios go on the outside edges.
79
+
80
+ ## Paper and finishing (say what you assume)
81
+
82
+ - Uncoated paper absorbs ink: colours print softer and darker, fine type spreads. Coated (gloss, silk, matt) keeps
83
+ detail. Ask, or state the assumption.
84
+ - Weights: flyers 130–170 gsm, posters 150–200 gsm, postcards and covers 300–350 gsm, business cards 350–450 gsm.
85
+ - Spot UV, foil, emboss and die-cuts need a separate vector layer per effect in pure 100% K or a named spot
86
+ swatch: build it as its own named group and say the printer needs it as a separate plate.
87
+ - White ink, varnish and spot colours cannot be represented in an RGB export; describe them in the handoff.
88
+
89
+ ## Printer handoff checklist
90
+
91
+ 1. Trim size, orientation, page count and fold.
92
+ 2. Bleed (3 mm unless the printer says otherwise) and whether crop marks are on.
93
+ 3. Output DPI the export achieved (the receipt states it) and that colour is RGB for the printer's conversion.
94
+ 4. Paper, finish and any spot colours or special effects.
95
+ 5. The preflight verdict, and any warning you kept on purpose with its reason.
96
+ 6. The PDF and the composition link.
@@ -0,0 +1,90 @@
1
+ # Print formats
2
+
3
+ Generated from `lib/canvas/printFormats.ts` — do not edit by hand. Every id works as `canvas.add-artboard { preset }` and as a `frame.create` print preset. Units are canvas units at 96 per inch; bleed and safe are the defaults saved when a canvas has no print settings. A printer's own specification always governs.
4
+
5
+ ## Paper
6
+
7
+ | id | name | trim | units (w×h) | bleed / safe mm | fold | note |
8
+ |---|---|---|---|---|---|---|
9
+ | `a6` | A6 | 105×148 mm | 396.9×559.4 | 3 / 4 | — | Pocket flyer or postcard; body 8–9 pt. |
10
+ | `a5` | A5 | 148×210 mm | 559.4×793.7 | 3 / 5 | — | Flyer, booklet or notebook page; body 9–10 pt. Also: `a5-flyer`. |
11
+ | `a4` | A4 | 210×297 mm | 793.7×1122.5 | 3 / 5 | — | Letterhead, report, menu or flyer; body 9–11 pt, read at 40 cm. Also: `a4-flyer`, `menu`, `report`, `letterhead`. |
12
+ | `letter` | Letter | 8.5×11 in | 816×1056 | 3 / 5 | — | US flyer, letterhead or report; body 9–11 pt. Also: `flyer`, `us-letter`, `poster-letter`. |
13
+ | `legal` | Legal | 8.5×14 in | 816×1344 | 3 / 5 | — | Menus and long forms. |
14
+ | `half-letter` | Half Letter | 5.5×8.5 in | 528×816 | 3 / 5 | — | Booklet, zine or program page; body 9–10 pt. Also: `statement`, `book-5.5x8.5`, `zine`. |
15
+
16
+ ## Leaflet
17
+
18
+ | id | name | trim | units (w×h) | bleed / safe mm | fold | note |
19
+ |---|---|---|---|---|---|---|
20
+ | `dl` | DL leaflet | 99×210 mm | 374.2×793.7 | 3 / 5 | — | Rack card or compliment slip; one column of copy. Also: `rack-card`, `dl-flyer`. |
21
+ | `trifold-letter` | Tri-fold Letter | 11×8.5 in | 1056×816 | 3 / 5 | roll | Roll fold to 3.7×8.5 in: inside flap 2 mm narrower; panel 1 is the cover. Also: `brochure`, `trifold`. |
22
+ | `trifold-a4` | Tri-fold A4 | 297×210 mm | 1122.5×793.7 | 3 / 5 | roll | Roll fold to DL (~99×210 mm): inside flap 2 mm narrower. |
23
+ | `zfold-a4` | Z-fold A4 | 297×210 mm | 1122.5×793.7 | 3 / 5 | z | Concertina fold into three equal DL panels. |
24
+ | `bifold-a4` | Bi-fold A4 | 297×210 mm | 1122.5×793.7 | 3 / 5 | half | Folds once to A5: back cover left, front cover right. Also: `folded-a5`. |
25
+ | `bifold-letter` | Bi-fold Letter | 11×8.5 in | 1056×816 | 3 / 5 | half | Folds once to 5.5×8.5 in. |
26
+ | `gatefold-letter` | Gate-fold Letter | 11×8.5 in | 1056×816 | 3 / 5 | gate | Two flaps meet at the centre; the flaps are 1 mm narrower than a quarter. |
27
+
28
+ ## Poster
29
+
30
+ | id | name | trim | units (w×h) | bleed / safe mm | fold | note |
31
+ |---|---|---|---|---|---|---|
32
+ | `a3` | A3 | 297×420 mm | 1122.5×1587.4 | 3 / 5 | — | Small poster or spread; headline read at 1–2 m. Also: `a3-poster`. |
33
+ | `a2` | A2 | 420×594 mm | 1587.4×2245 | 3 / 10 | — | Poster read at 2–3 m; headline 72 pt or more. |
34
+ | `a1` | A1 | 594×841 mm | 2245×3178.6 | 3 / 10 | — | Poster or A-board read at 3–5 m. Also: `a-board`, `a-frame`. |
35
+ | `a0` | A0 | 841×1189 mm | 3178.6×4493.9 | 3 / 15 | — | Large poster or conference board; read at 5 m or more. Also: `conference-poster`. |
36
+ | `b4` | B4 | 250×353 mm | 944.9×1334.2 | 3 / 5 | — | Poster or large booklet. |
37
+ | `tabloid` | Tabloid 11×17 | 11×17 in | 1056×1632 | 3 / 5 | — | Small poster or newsletter spread. Also: `ledger`, `poster-11x17`, `11x17`. |
38
+ | `poster-18x24` | Poster 18×24 in | 18×24 in | 1728×2304 | 3 / 10 | — | Event poster read at 2–4 m. |
39
+ | `poster-24x36` | Poster 24×36 in | 24×36 in | 2304×3456 | 3 / 12 | — | Large poster read at 3–6 m. |
40
+ | `poster-27x40` | One-sheet 27×40 in | 27×40 in | 2592×3840 | 3 / 12 | — | Film one-sheet; billing block at the foot. Also: `one-sheet`, `movie-poster`. |
41
+ | `poster-50x70` | Poster 50×70 cm | 500×700 mm | 1889.8×2645.7 | 3 / 10 | — | European poster read at 3–5 m. |
42
+ | `poster-70x100` | Poster 70×100 cm | 700×1000 mm | 2645.7×3779.5 | 3 / 12 | — | Large European poster. |
43
+
44
+ ## Card
45
+
46
+ | id | name | trim | units (w×h) | bleed / safe mm | fold | note |
47
+ |---|---|---|---|---|---|---|
48
+ | `business-card` | Business card 3.5×2 in | 3.5×2 in | 336×192 | 3 / 3 | — | US card; name 9–11 pt, details 7–8 pt, 3 mm safe. Also: `business-cards`, `card`, `business-card-us`. |
49
+ | `business-card-eu` | Business card 85×55 mm | 85×55 mm | 321.3×207.9 | 3 / 3 | — | European card; details 7–8 pt. |
50
+ | `business-card-jp` | Business card 91×55 mm | 91×55 mm | 343.9×207.9 | 3 / 3 | — | Japanese meishi. |
51
+ | `postcard` | Postcard 6×4 in | 6×4 in | 576×384 | 3 / 4 | — | Landscape postcard; leave the back address half clear. Also: `postcard-6x4`, `4x6`. |
52
+ | `postcard-5x7` | Postcard 7×5 in | 7×5 in | 672×480 | 3 / 4 | — | Invitation or large postcard. Also: `invitation`, `5x7`. |
53
+ | `postcard-a6` | Postcard A6 | 148×105 mm | 559.4×396.9 | 3 / 4 | — | European landscape postcard. |
54
+ | `greeting-card` | Greeting card 5×7 folded | 10×7 in | 960×672 | 3 / 4 | half | Flat sheet folding to 5×7 in portrait: back panel left, front right. |
55
+ | `bookmark` | Bookmark 2×6 in | 2×6 in | 192×576 | 3 / 3 | — | One column; 6–8 pt details. |
56
+ | `ticket` | Ticket 5.5×2 in | 5.5×2 in | 528×192 | 3 / 3 | — | Event ticket; stub on the right. |
57
+
58
+ ## Book
59
+
60
+ | id | name | trim | units (w×h) | bleed / safe mm | fold | note |
61
+ |---|---|---|---|---|---|---|
62
+ | `b5` | B5 | 176×250 mm | 665.2×944.9 | 3 / 5 | — | Journal, workbook or magazine. |
63
+ | `book-5x8` | Book 5×8 in | 5×8 in | 480×768 | 3 / 5 | — | Trade paperback; inner margin largest for the binding. |
64
+ | `book-6x9` | Book 6×9 in | 6×9 in | 576×864 | 3 / 5 | — | Trade paperback or report; body 10–11.5 pt. |
65
+ | `book-b-format` | B-format 129×198 mm | 129×198 mm | 487.6×748.3 | 3 / 5 | — | UK paperback. |
66
+ | `book-royal` | Royal 156×234 mm | 156×234 mm | 589.6×884.4 | 3 / 5 | — | Hardback or monograph. |
67
+
68
+ ## Sticker
69
+
70
+ | id | name | trim | units (w×h) | bleed / safe mm | fold | note |
71
+ |---|---|---|---|---|---|---|
72
+ | `sticker2` | Sticker 2 in | 2×2 in | 192×192 | 2 / 3 | — | Die-cut sticker; 2 mm bleed past the cut. |
73
+ | `sticker3` | Sticker 3 in | 3×3 in | 288×288 | 2 / 3 | — | Die-cut sticker; 2 mm bleed past the cut. |
74
+ | `sticker4` | Sticker 4 in | 4×4 in | 384×384 | 2 / 3 | — | Die-cut sticker or label. |
75
+ | `square1080` | Square 1080 at 300 dpi | 3.6×3.6 in | 345.6×345.6 | 0 / 3 | — | A 1080-px square exported at 300 dpi. |
76
+
77
+ ## Signage
78
+
79
+ | id | name | trim | units (w×h) | bleed / safe mm | fold | note |
80
+ |---|---|---|---|---|---|---|
81
+ | `roll-up` | Roll-up banner 85×200 cm | 850×2000 mm | 3212.6×7559.1 | 5 / 25 | — | Bottom 150 mm hides in the stand; keep copy above 1 m. Also: `banner`, `pull-up`. |
82
+ | `yard-sign` | Yard sign 24×18 in | 24×18 in | 2304×1728 | 3 / 15 | — | Read from a moving car: five words, huge type. |
83
+
84
+ ## Envelope
85
+
86
+ | id | name | trim | units (w×h) | bleed / safe mm | fold | note |
87
+ |---|---|---|---|---|---|---|
88
+ | `envelope-dl` | Envelope DL | 220×110 mm | 831.5×415.7 | 0 / 10 | — | Address window conventions vary: ask the printer. |
89
+ | `envelope-c5` | Envelope C5 | 229×162 mm | 865.5×612.3 | 0 / 10 | — | Holds A5 or folded A4. |
90
+ | `envelope-10` | Envelope #10 | 9.5×4.126 in | 912×396.1 | 0 / 10 | — | US business envelope. |
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: social-posts
3
+ description: >-
4
+ Instagram feed posts and carousels, stories, reel covers, thumbnails and banners on a Clearly canvas: platform sizes (frame presets), safe zones, phone-readable type, carousel structure, one design rebuilt for every format, and PNG export. Load it before building any social post, carousel, story, thumbnail, banner or ad, or to adapt a design to other platform sizes. Triggers: "instagram post", "carousel", "story", "reel cover", "youtube thumbnail", "linkedin banner", "resize this for every platform". Needs a terminal with the beehaven CLI; in a chat with only the Clearly connector, use the clearly_* tools.
5
+ ---
6
+
7
+ > **Canvas work goes through the CLI.** Every verb below is a `canvas-act` action:
8
+ > `beehaven call canvas-act '{"compositionId":"<id>","action":"frame.create","args":{"name":"Launch · IG 4:5","preset":"social-portrait"}}'`,
9
+ > or several at once with `{"compositionId":"<id>","batch":[{"action":…,"args":{…}}, …]}`. `brand-tokens`
10
+ > and `canvas-perceive` are `beehaven call <name> '{…}'` too. Clearly runs no AI model for a connected
11
+ > agent (MCP2-57): no image generation, editing or upscaling here — place photos the person supplies.
12
+ > The in-app agent's `social-posts` house skill (apps/cloudflare/src/agent/media-skills.ts) shares this text
13
+ > except for its AI image steps.
14
+
15
+ # social-posts
16
+
17
+ Social posts on the Clearly canvas: Instagram feed posts and carousels, stories and reels covers, thumbnails and banners, and one design adapted to every format.
18
+
19
+ SIZES (frame.create { name, preset })
20
+ - Feed: "social-portrait" 1080×1350 (4:5, the most screen space; the default for a feed post), "social-post" 1080×1080, "social-grid" 1080×1440 (3:4).
21
+ - Story / reel / TikTok cover: "social-story" 1080×1920.
22
+ - "youtube-thumbnail" 1280×720, "pinterest-pin" 1000×1500, "linkedin-post" 1200×627, "x-post" 1600×900, "linkedin-banner", "youtube-banner", "facebook-cover".
23
+ Name artboards "<campaign> · IG 4:5", "<campaign> · Story".
24
+
25
+ SAFE ZONES (keep text, logos and faces inside)
26
+ - Story/reel 1080×1920: nothing important in the top 250 px or the bottom 340 px (the app's own UI covers them).
27
+ - The Instagram profile grid shows a 3:4 centre crop: on a 1:1 post keep key content inside the centre 810 px; on 4:5, inside the centre ~1012 px.
28
+ - YouTube thumbnail: the duration badge covers the bottom-right corner.
29
+ - Margins: at least 64 px from every edge on 1080-wide formats.
30
+
31
+ TYPE THAT READS ON A PHONE
32
+ A 1080 px post is seen at about a third of its size. Headline 72–120 px, at most about 7 words; supporting line 36–48 px; small print 28 px minimum. One typeface family, two weights. Measure with canvas.measure-text before placing.
33
+
34
+ LAYOUT
35
+ One focal point per slide: a photo or illustration, one headline, one small brand mark. Brand colours from brand-tokens. Text over a photo needs a calm area, a scrim or a plate. Leave the logo small, in a corner, never centred.
36
+
37
+ CAROUSELS
38
+ 2–10 slides of the same size, built as artboards side by side. Slide 1 is the hook (the claim or the question), the middle slides each carry one idea, the last is the call to action. Repeat the same grid, margins and type sizes on every slide. For continuity, carry one shape or line across a seam: draw its two halves at the matching edge of each slide.
39
+
40
+ ONE DESIGN, EVERY FORMAT
41
+ Design the master first (usually 4:5). For each other format, create its artboard from the preset and REBUILD the layout: same hierarchy and content, re-flowed for the new shape. Do not just scale. Move a photo's focus with cropX/cropY so the subject stays in frame, re-wrap the headline to the new width, and keep sizes relative to the short side. canvas.duplicate { ids, dx, name, toW } only for the same aspect at another size (for example 1080 → 2160).
42
+
43
+ CHECK AND DELIVER
44
+ - canvas.review each artboard until clean or look; open its image and check the safe zones and legibility.
45
+ - Export: canvas.export { format:"png", ids:[artboard] } gives a 1080-px file link per artboard (scale:2 for a sharper upload).
46
+ - Offer a caption (the first line is the hook), alt text describing the image, and hashtags only if asked.
@@ -4,13 +4,12 @@ description: Needs a terminal with the beehaven CLI (Claude Code, Codex, Gemini
4
4
  "explain visually", "show me how X works", "flowchart this", "make a diagram" — AND proactively whenever YOUR OWN answer would be clearer as a picture than a paragraph (a flow with branches, who-calls-whom, a schema, an architecture, options on axes). Picks the right diagram type for the content and builds it with the canvas primitives.
5
5
  ---
6
6
 
7
- > **⚠ CANVAS WORK GOES THROUGH THE CLI, NOT AN MCP TOOL.** Anything written
7
+ > **⚠ IN A SHELL, CANVAS WORK GOES THROUGH THE CLI.** Anything written
8
8
  > `canvas-act { … }` / `canvas-perceive { … }` below is an ACTION, run as
9
- > `beehaven call canvas-act '<the JSON>'`. The payloads are exactly as shown. The MCP
10
- > surface is seven tools split by operation, and a single tool that dispatches 230
11
- > caller-named canvas actions spans reads, writes and deletes at once — which is why the
12
- > generic door is the shell instead. `clearly_read { target, type: "composition" }` still
13
- > reads a canvas as an artifact.
9
+ > `beehaven call canvas-act '<the JSON>'`. The payloads are exactly as shown. One CLI batch can
10
+ > mix reads and writes, so a build is fewer calls. Without a shell, the MCP connector's six canvas
11
+ > tools do the same work, one safety class each: `clearly_canvas_add` takes the same node
12
+ > objects, `clearly_canvas_update` the same patches and ops, `clearly_canvas_review` the review.
14
13
 
15
14
  # Visualize — draw it, don't write it
16
15