wand-kit 0.3.2 → 0.5.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 (162) hide show
  1. package/PROMPT.md +221 -19
  2. package/SKILL.md +100 -20
  3. package/START-HERE.md +12 -1
  4. package/brands/current/tokens.json +10 -2
  5. package/brands/interim/assets/avatar.svg +1 -0
  6. package/brands/interim/assets/closing-art.png +0 -0
  7. package/brands/interim/assets/cover-art.png +0 -0
  8. package/brands/interim/assets/cover-portrait-art.png +0 -0
  9. package/brands/interim/assets/divider-art.png +0 -0
  10. package/brands/interim/assets/glyphs/README.md +29 -0
  11. package/brands/interim/assets/glyphs/activity.svg +15 -0
  12. package/brands/interim/assets/glyphs/atom.svg +17 -0
  13. package/brands/interim/assets/glyphs/badge-check.svg +16 -0
  14. package/brands/interim/assets/glyphs/bell.svg +16 -0
  15. package/brands/interim/assets/glyphs/blocks.svg +16 -0
  16. package/brands/interim/assets/glyphs/book-open.svg +16 -0
  17. package/brands/interim/assets/glyphs/bot.svg +20 -0
  18. package/brands/interim/assets/glyphs/boxes.svg +26 -0
  19. package/brands/interim/assets/glyphs/brain.svg +23 -0
  20. package/brands/interim/assets/glyphs/briefcase.svg +16 -0
  21. package/brands/interim/assets/glyphs/building-2.svg +21 -0
  22. package/brands/interim/assets/glyphs/calendar.svg +18 -0
  23. package/brands/interim/assets/glyphs/chart-column.svg +18 -0
  24. package/brands/interim/assets/glyphs/chart-line.svg +16 -0
  25. package/brands/interim/assets/glyphs/chart-pie.svg +16 -0
  26. package/brands/interim/assets/glyphs/circle-check.svg +16 -0
  27. package/brands/interim/assets/glyphs/clipboard-check.svg +17 -0
  28. package/brands/interim/assets/glyphs/clock.svg +16 -0
  29. package/brands/interim/assets/glyphs/cloud.svg +15 -0
  30. package/brands/interim/assets/glyphs/code.svg +15 -0
  31. package/brands/interim/assets/glyphs/coins.svg +18 -0
  32. package/brands/interim/assets/glyphs/compass.svg +16 -0
  33. package/brands/interim/assets/glyphs/cpu.svg +24 -0
  34. package/brands/interim/assets/glyphs/database.svg +17 -0
  35. package/brands/interim/assets/glyphs/eye.svg +16 -0
  36. package/brands/interim/assets/glyphs/file-check.svg +17 -0
  37. package/brands/interim/assets/glyphs/filter.svg +15 -0
  38. package/brands/interim/assets/glyphs/fingerprint.svg +23 -0
  39. package/brands/interim/assets/glyphs/flag.svg +16 -0
  40. package/brands/interim/assets/glyphs/gauge.svg +16 -0
  41. package/brands/interim/assets/glyphs/gavel.svg +19 -0
  42. package/brands/interim/assets/glyphs/git-branch.svg +18 -0
  43. package/brands/interim/assets/glyphs/globe.svg +17 -0
  44. package/brands/interim/assets/glyphs/graduation-cap.svg +17 -0
  45. package/brands/interim/assets/glyphs/handshake.svg +19 -0
  46. package/brands/interim/assets/glyphs/hexagon.svg +15 -0
  47. package/brands/interim/assets/glyphs/index.json +177 -0
  48. package/brands/interim/assets/glyphs/infinity.svg +15 -0
  49. package/brands/interim/assets/glyphs/key.svg +17 -0
  50. package/brands/interim/assets/glyphs/landmark.svg +20 -0
  51. package/brands/interim/assets/glyphs/layers.svg +17 -0
  52. package/brands/interim/assets/glyphs/lightbulb.svg +17 -0
  53. package/brands/interim/assets/glyphs/link.svg +16 -0
  54. package/brands/interim/assets/glyphs/list-checks.svg +19 -0
  55. package/brands/interim/assets/glyphs/lock.svg +16 -0
  56. package/brands/interim/assets/glyphs/map.svg +17 -0
  57. package/brands/interim/assets/glyphs/megaphone.svg +16 -0
  58. package/brands/interim/assets/glyphs/message-square.svg +15 -0
  59. package/brands/interim/assets/glyphs/milestone.svg +17 -0
  60. package/brands/interim/assets/glyphs/network.svg +19 -0
  61. package/brands/interim/assets/glyphs/orbit.svg +19 -0
  62. package/brands/interim/assets/glyphs/package.svg +18 -0
  63. package/brands/interim/assets/glyphs/plug.svg +18 -0
  64. package/brands/interim/assets/glyphs/puzzle.svg +15 -0
  65. package/brands/interim/assets/glyphs/radar.svg +22 -0
  66. package/brands/interim/assets/glyphs/receipt.svg +17 -0
  67. package/brands/interim/assets/glyphs/refresh-cw.svg +18 -0
  68. package/brands/interim/assets/glyphs/rocket.svg +18 -0
  69. package/brands/interim/assets/glyphs/route.svg +17 -0
  70. package/brands/interim/assets/glyphs/scale.svg +19 -0
  71. package/brands/interim/assets/glyphs/search.svg +16 -0
  72. package/brands/interim/assets/glyphs/server.svg +18 -0
  73. package/brands/interim/assets/glyphs/settings.svg +16 -0
  74. package/brands/interim/assets/glyphs/share-2.svg +19 -0
  75. package/brands/interim/assets/glyphs/shield-check.svg +16 -0
  76. package/brands/interim/assets/glyphs/shuffle.svg +19 -0
  77. package/brands/interim/assets/glyphs/sparkles.svg +19 -0
  78. package/brands/interim/assets/glyphs/square-stack.svg +17 -0
  79. package/brands/interim/assets/glyphs/target.svg +17 -0
  80. package/brands/interim/assets/glyphs/trending-up.svg +16 -0
  81. package/brands/interim/assets/glyphs/triangle-alert.svg +17 -0
  82. package/brands/interim/assets/glyphs/trophy.svg +20 -0
  83. package/brands/interim/assets/glyphs/users.svg +18 -0
  84. package/brands/interim/assets/glyphs/wallet.svg +16 -0
  85. package/brands/interim/assets/glyphs/wand-sparkles.svg +22 -0
  86. package/brands/interim/assets/glyphs/workflow.svg +17 -0
  87. package/brands/interim/assets/glyphs/zap.svg +15 -0
  88. package/brands/interim/assets/mark.svg +1 -0
  89. package/brands/interim/assets/social/composition.json +6833 -0
  90. package/brands/interim/assets/spark-lavender.png +0 -0
  91. package/brands/interim/assets/spark-violet.png +0 -0
  92. package/brands/interim/assets/statement-art.png +0 -0
  93. package/brands/interim/assets/story/agent-alpha.png +0 -0
  94. package/brands/interim/assets/story/agent.png +0 -0
  95. package/brands/interim/assets/story/economy-alpha.png +0 -0
  96. package/brands/interim/assets/story/economy.png +0 -0
  97. package/brands/interim/assets/story/index.json +214 -0
  98. package/brands/interim/assets/story/learning-alpha.png +0 -0
  99. package/brands/interim/assets/story/learning.png +0 -0
  100. package/brands/interim/assets/story/organization-alpha.png +0 -0
  101. package/brands/interim/assets/story/organization.png +0 -0
  102. package/brands/interim/assets/story/worker-alpha.png +0 -0
  103. package/brands/interim/assets/story/worker-light.png +0 -0
  104. package/brands/interim/assets/story/worker.png +0 -0
  105. package/brands/interim/assets/wordmark-on-dark.png +0 -0
  106. package/brands/interim/assets/wordmark-on-dark.svg +1 -0
  107. package/brands/interim/assets/wordmark-on-light.png +0 -0
  108. package/brands/interim/assets/wordmark-on-light.svg +1 -0
  109. package/brands/interim/fonts/Geist-Book.ttf +0 -0
  110. package/brands/interim/fonts/Geist-Medium.ttf +0 -0
  111. package/brands/interim/fonts/Geist-Regular.ttf +0 -0
  112. package/brands/interim/fonts/Geist-SemiBold.ttf +0 -0
  113. package/brands/interim/fonts/Newsreader-Italic.ttf +0 -0
  114. package/brands/interim/fonts/Newsreader-Medium.ttf +0 -0
  115. package/brands/interim/fonts/Newsreader-MediumItalic.ttf +0 -0
  116. package/brands/interim/fonts/Newsreader-Regular.ttf +0 -0
  117. package/brands/interim/fonts/OFL-Geist.txt +93 -0
  118. package/brands/interim/fonts/OFL-Newsreader.txt +93 -0
  119. package/brands/interim/fonts/faces.json +12 -0
  120. package/brands/interim/lib/composition.js +169 -0
  121. package/brands/interim/lib/composition.mjs +101 -0
  122. package/brands/interim/lib/glyphs.js +89 -0
  123. package/brands/interim/lib/measure.js +121 -0
  124. package/brands/interim/lib/pdf.js +597 -0
  125. package/brands/interim/lib/ttf.py +169 -0
  126. package/brands/interim/lib/wandxml.py +37 -0
  127. package/brands/interim/tokens.json +299 -0
  128. package/brands/next/tokens.json +14 -2
  129. package/examples/social/social.json +23 -0
  130. package/lib/audit.js +142 -0
  131. package/lib/brand.js +12 -7
  132. package/lib/components.js +42 -10
  133. package/lib/engine.js +72 -9
  134. package/lib/library.js +180 -0
  135. package/lib/pptx.js +1 -1
  136. package/lib/social.js +366 -0
  137. package/lib/talk.js +37 -4
  138. package/library/index.json +629 -0
  139. package/library/interim/avatar-800x800.png +0 -0
  140. package/library/interim/icons/apple-touch-icon.png +0 -0
  141. package/library/interim/icons/favicon-16.png +0 -0
  142. package/library/interim/icons/favicon-32.png +0 -0
  143. package/library/interim/icons/favicon-48.png +0 -0
  144. package/library/interim/icons/favicon.ico +0 -0
  145. package/library/interim/icons/favicon.svg +1 -0
  146. package/library/interim/icons/icon-192.png +0 -0
  147. package/library/interim/icons/icon-512.png +0 -0
  148. package/library/interim/icons/icon-tags.html +5 -0
  149. package/library/interim/icons/site.webmanifest +19 -0
  150. package/library/interim/linkedin-banner-1584x396.png +0 -0
  151. package/library/interim/linkedin-company-1128x191.png +0 -0
  152. package/library/interim/open-agent-discovery-share-1200x630.png +0 -0
  153. package/library/interim/open-agent-discovery-share-head-tags.html +17 -0
  154. package/library/interim/wand-ai-share-1200x630.png +0 -0
  155. package/library/interim/wand-ai-share-head-tags.html +17 -0
  156. package/library/interim/x-header-1500x500.png +0 -0
  157. package/package.json +2 -2
  158. package/templates/card.mjs +6 -2
  159. package/templates/page.mjs +1 -1
  160. package/templates/slide.mjs +1 -1
  161. package/templates/storyboard.mjs +1 -1
  162. package/wand.js +65 -6
@@ -0,0 +1,17 @@
1
+ <!-- https://wand.ai/insights/open-agent-discovery-nandaindex.html -->
2
+ <meta property="og:type" content="article">
3
+ <meta property="og:site_name" content="Wand">
4
+ <meta property="og:title" content="An Open Agentic Web Needs Open Discovery | Wand AI">
5
+ <meta property="og:description" content="Why interoperable agent discovery matters for the emerging agentic economy, and how NandaIndex complements governed AI workforces.">
6
+ <meta property="og:url" content="https://wand.ai/insights/open-agent-discovery-nandaindex.html">
7
+ <meta property="og:image" content="https://wand.ai/assets/og/20261023/open-agent-discovery-share-1200x630.png">
8
+ <meta property="og:image:secure_url" content="https://wand.ai/assets/og/20261023/open-agent-discovery-share-1200x630.png">
9
+ <meta property="og:image:type" content="image/png">
10
+ <meta property="og:image:width" content="1200">
11
+ <meta property="og:image:height" content="630">
12
+ <meta property="og:image:alt" content="Wand. An Open Agentic Web Needs Open Discovery.">
13
+ <meta name="twitter:card" content="summary_large_image">
14
+ <meta name="twitter:title" content="An Open Agentic Web Needs Open Discovery | Wand AI">
15
+ <meta name="twitter:description" content="Why interoperable agent discovery matters for the emerging agentic economy, and how NandaIndex complements governed AI workforces.">
16
+ <meta name="twitter:image" content="https://wand.ai/assets/og/20261023/open-agent-discovery-share-1200x630.png">
17
+ <meta name="twitter:image:alt" content="Wand. An Open Agentic Web Needs Open Discovery.">
@@ -0,0 +1,17 @@
1
+ <!-- https://wand.ai/ -->
2
+ <meta property="og:type" content="website">
3
+ <meta property="og:site_name" content="Wand">
4
+ <meta property="og:title" content="Wand AI">
5
+ <meta property="og:description" content="Autonomous AI workers. One operating system for people and agents. The foundation for a new era of enterprise and national capacity.">
6
+ <meta property="og:url" content="https://wand.ai/">
7
+ <meta property="og:image" content="https://wand.ai/assets/og/20261023/wand-ai-share-1200x630.png">
8
+ <meta property="og:image:secure_url" content="https://wand.ai/assets/og/20261023/wand-ai-share-1200x630.png">
9
+ <meta property="og:image:type" content="image/png">
10
+ <meta property="og:image:width" content="1200">
11
+ <meta property="og:image:height" content="630">
12
+ <meta property="og:image:alt" content="Wand. A new workforce. A new operating era.">
13
+ <meta name="twitter:card" content="summary_large_image">
14
+ <meta name="twitter:title" content="Wand AI">
15
+ <meta name="twitter:description" content="Autonomous AI workers. One operating system for people and agents. The foundation for a new era of enterprise and national capacity.">
16
+ <meta name="twitter:image" content="https://wand.ai/assets/og/20261023/wand-ai-share-1200x630.png">
17
+ <meta name="twitter:image:alt" content="Wand. A new workforce. A new operating era.">
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wand-kit",
3
- "version": "0.3.2",
4
- "description": "One HTML design surface for every sales and marketing material, on either Wand brand: decks that animate and present, documents, one-pagers, case studies, storyboards from a script, social cards. Every export (PDF, PNG, PowerPoint, one-file HTML) is read from the page that was reviewed.",
3
+ "version": "0.5.0",
4
+ "description": "One HTML design surface for every sales and marketing material, on any of the three Wand brands: decks that animate and present, documents, one-pagers, case studies, storyboards from a script, social images. Every export (PDF, PNG, PowerPoint, one-file HTML) is read from the page that was reviewed, and the files people approved (banners, link previews, icons, logos, art) are handed out as they are.",
5
5
  "license": "SEE LICENSE IN PROMPT.md",
6
6
  "bin": {
7
7
  "wand-kit": "./wand.js"
@@ -2,17 +2,21 @@
2
2
  * __NAME__ — social cards on the Wand studio (square 1080 × 1080, or og 1200 × 630).
3
3
  *
4
4
  * One `c.page()` per card. Each page becomes a PNG in out/pages/; the PDF
5
- * carries them all. `css` may only use the brand's custom properties.
5
+ * carries them all. `css` may only use the brand's custom properties. On the
6
+ * interim brand, banners and link previews come from its approved composition
7
+ * instead: `node wand.js social` (PROMPT.md §14).
6
8
  */
7
9
  export default {
8
10
  name: "__NAME__",
9
- brand: "__BRAND__", // current | next
11
+ brand: "__BRAND__", // current | next | interim
10
12
  surface: "__SURFACE__", // square | og
11
13
  tag: "Wand",
12
14
  footer: "wand.ai",
13
15
  css: `
14
16
  .quote-card .content{justify-content:center;gap:1.2em}
15
17
  .quote-card .lede{font-size:var(--t-h2);line-height:1.3;max-width:100%}
18
+ /* a share image (og) keeps its words in the centre square: Slack and WhatsApp cut their thumbnail there */
19
+ .surface-og .quote-card .content{left:309px;right:309px}
16
20
  `,
17
21
  pages: (c) => [
18
22
  c.page({ dark: true, art: "cover", eyebrow: "Announcement", cls: "quote-card", body: `
@@ -11,7 +11,7 @@
11
11
  */
12
12
  export default {
13
13
  name: "__NAME__",
14
- brand: "__BRAND__", // current | next
14
+ brand: "__BRAND__", // current | next | interim
15
15
  surface: "__SURFACE__", // letter | a4
16
16
  tag: "Wand", // the running tag, left of the eyebrow
17
17
  footer: "Wand AI · Confidential",
@@ -8,7 +8,7 @@
8
8
  */
9
9
  export default {
10
10
  name: "__NAME__",
11
- brand: "__BRAND__", // current | next
11
+ brand: "__BRAND__", // current | next | interim
12
12
  surface: "slide",
13
13
  tag: "Wand × Client",
14
14
  footer: "Wand AI · Strictly confidential",
@@ -10,7 +10,7 @@
10
10
  */
11
11
  export default {
12
12
  name: "__NAME__",
13
- brand: "__BRAND__", // current | next
13
+ brand: "__BRAND__", // current | next | interim
14
14
  surface: "slide",
15
15
  tag: "Wand · Storyboard",
16
16
  footer: "Wand AI · Draft for review",
package/wand.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * wand.js — the studio's front door.
4
4
  *
5
5
  * node wand.js doctor what works here, and the brand rule
6
- * node wand.js new <dir> [--surface letter] [--brand current|next] [--name "Title"]
6
+ * node wand.js new <dir> [--surface letter] [--brand current|next|interim] [--name "Title"]
7
7
  * a job folder to compose in, on the default brand
8
8
  * unless --brand names the other
9
9
  * node wand.js new <dir> --surface storyboard a storyboard from a script (shots, frames, timing)
@@ -14,7 +14,17 @@
14
14
  * like a presenter would before it ships
15
15
  * node wand.js poster <demo.html> [--play 4] a demo's poster: the frame every static export shows
16
16
  * node wand.js watch <job.mjs> rebuild on save, the browser kept warm
17
- * node wand.js gallery [--brand current|next] every component, light and dark, on one surface each
17
+ * node wand.js social <social.json> [--brand interim] the social set drawn from the brand's approved composition:
18
+ * banners, share images and their tags, a square, the avatar,
19
+ * a profile photo that continues the banner, the site's icons;
20
+ * INSTALL.md and a zip of the hand-over (interim only)
21
+ * node wand.js social --audit <url | page.html> [more] [--image og.png]
22
+ * a page's link preview as it is today: its tags, its image,
23
+ * and that image the way the feeds cut and shrink it
24
+ * node wand.js library [words] [--brand interim] the approved files: banners, link previews, icons, logos, art
25
+ * node wand.js library get <id> [<id>…] [--to dir] hand one over as it is (no browser needed)
26
+ * node wand.js library --check every file the one approved, and the kit still draws it
27
+ * node wand.js gallery [--brand current|next|interim] every component, light and dark, on one surface each
18
28
  * node wand.js components the vocabulary
19
29
  * node wand.js brands the brands this kit can draw with
20
30
  *
@@ -51,6 +61,7 @@ async function doctor() {
51
61
  const py = ["python3", "python"].find((c) => spawnSync(c, ["-c", "import sys;print(sys.version_info[0])"], { encoding: "utf8" }).stdout?.trim() === "3");
52
62
  rows.push(["python3", py ? execFileSync(py, ["-V"], { encoding: "utf8" }).trim() : "absent", py ? "ok" : "the .pptx ships without embedded fonts"]);
53
63
  rows.push(["pdftotext", has("pdftotext") ? "found" : "absent", has("pdftotext") ? "ok" : "coverage is read from the DOM only (poppler adds the PDF read-back)"]);
64
+ try { const { load } = await import("./lib/library.js"); rows.push(["library", `${load().assets.length} approved assets`, "ok"]); } catch { rows.push(["library", "absent", "no approved files to hand out"]); }
54
65
  const w = Math.max(...rows.map((r) => r[0].length));
55
66
  console.log("");
56
67
  for (const [k, v, s] of rows) console.log(`${s === "ok" ? " " : "! "}${k.padEnd(w)} ${String(v).padEnd(36)}${s === "ok" ? "" : s}`);
@@ -61,7 +72,7 @@ async function doctor() {
61
72
  }
62
73
 
63
74
  function newJob() {
64
- const dir = args[1]; if (!dir) die("usage: node wand.js new <dir> [--surface letter|a4|slide|square|og] [--brand current|next] [--name \"Title\"]");
75
+ const dir = args[1]; if (!dir) die("usage: node wand.js new <dir> [--surface letter|a4|slide|square|og] [--brand current|next|interim] [--name \"Title\"]");
65
76
  const surface = flag("--surface", "letter"), brand = flag("--brand", DEFAULT_BRAND), name = flag("--name", path.basename(dir));
66
77
  if (!BRANDS.includes(brand)) die(`unknown brand "${brand}" - one of ${BRANDS.join(", ")}`);
67
78
  const tpl = path.join(KIT, "templates", `${surface === "storyboard" ? "storyboard" : ["slide", "square", "og"].includes(surface) ? (surface === "slide" ? "slide" : "card") : "page"}.mjs`);
@@ -82,7 +93,7 @@ function brandFlag() {
82
93
  }
83
94
 
84
95
  async function build(jobPath, opts) {
85
- if (!jobPath) die("usage: node wand.js build <job.mjs> [--brand current|next]");
96
+ if (!jobPath) die("usage: node wand.js build <job.mjs> [--brand current|next|interim]");
86
97
  const { build } = await import("./lib/engine.js");
87
98
  return build(jobPath, { ...opts, brand: brandFlag() });
88
99
  }
@@ -143,7 +154,7 @@ async function watch(jobPath) {
143
154
 
144
155
  /** A demo's poster: the page at the size it is shown, after it settles, optionally after it plays. */
145
156
  async function poster(demoPath) {
146
- if (!demoPath) die("usage: node wand.js poster <demo.html> [--brand current|next] [--size 1280x720] [--wait 1.5] [--play 0] [--out poster.png]");
157
+ if (!demoPath) die("usage: node wand.js poster <demo.html> [--brand current|next|interim] [--size 1280x720] [--wait 1.5] [--play 0] [--out poster.png]");
147
158
  const { browser, closeBrowser } = await import("./lib/engine.js");
148
159
  const [w, h] = flag("--size", "1280x720").split("x").map(Number);
149
160
  const out = flag("--out", demoPath.replace(/\.html?$/i, "") + ".poster.png");
@@ -174,6 +185,52 @@ async function gallery() {
174
185
  return r.ok ? 0 : 1;
175
186
  }
176
187
 
188
+ async function social(specPath) {
189
+ const { closeBrowser } = await import("./lib/engine.js");
190
+ if (on("--audit")) {
191
+ const { audit } = await import("./lib/audit.js");
192
+ const skip = new Set(["--audit", flag("--image"), flag("--out")]);
193
+ const targets = args.slice(1).filter((a, i, all) => !skip.has(a) && !["--image", "--out"].includes(all[i - 1]) && !a.startsWith("--"));
194
+ const r = await audit(targets, { image: flag("--image"), out: flag("--out", "audit") });
195
+ await closeBrowser();
196
+ return r.ok ? 0 : 1;
197
+ }
198
+ if (!specPath) die("usage: node wand.js social <social.json> [--brand interim] (see examples/social/social.json)\n node wand.js social --audit <url | page.html> [more] [--image og.png] [--out audit]");
199
+ const { social } = await import("./lib/social.js");
200
+ const r = await social(specPath, { brand: brandFlag() });
201
+ await closeBrowser();
202
+ return r.ok ? 0 : 1;
203
+ }
204
+
205
+ /** The approved files: list them, hand them over as they are, or prove they are still the ones approved. */
206
+ async function library() {
207
+ const lib = await import("./lib/library.js");
208
+ if (on("--check")) {
209
+ const r = await lib.check();
210
+ const { closeBrowser } = await import("./lib/engine.js"); await closeBrowser();
211
+ return r.ok ? 0 : 1;
212
+ }
213
+ const brand = brandFlag();
214
+ const words = args.slice(1).filter((a, i, all) => !a.startsWith("--") && !["--brand", "--to"].includes(all[i - 1]));
215
+ if (words[0] === "get") {
216
+ if (!words[1]) die("usage: node wand.js library get <id> [<id>…] [--to dir] [--brand interim] (node wand.js library lists the ids)");
217
+ const { done, missing } = lib.get(words.slice(1), { to: flag("--to", "wand-assets"), brand });
218
+ for (const { asset, files } of done) {
219
+ console.log(`\n ${asset.id} ${asset.title} (${asset.brand} brand)\n use ${asset.use}\n approved ${asset.approved}`);
220
+ const show = (p) => (rel(p).startsWith("..") ? p : rel(p));
221
+ for (const f of files) console.log(` ${show(f.dest).padEnd(52)} ${String(f.px || "").padEnd(24)} ${Math.max(1, Math.round(f.bytes / 1024))} KB`);
222
+ }
223
+ for (const m of missing) console.log(`\n ${m.id}: ${m.cands.length ? `which one? ${m.cands.join(", ")}` : "no such asset; node wand.js library lists them"}`);
224
+ console.log("");
225
+ return missing.length ? 2 : 0;
226
+ }
227
+ const found = lib.list(words, { brand });
228
+ console.log("");
229
+ for (const a of found) console.log(` ${a.id.padEnd(28)} ${a.brand.padEnd(8)} ${a.title}${a.size ? `, ${a.size}` : ""}`);
230
+ console.log(found.length ? `\n ${found.length} of ${lib.load().assets.length} approved assets. Hand one over: node wand.js library get <id> --to <folder>\n` : ` nothing matches ${words.join(" ")}; node wand.js library lists everything\n`);
231
+ return 0;
232
+ }
233
+
177
234
  async function components() {
178
235
  const { bind } = await import("./lib/components.js");
179
236
  const { loadBrand } = await import("./lib/brand.js");
@@ -185,7 +242,7 @@ async function components() {
185
242
  tag: "tag(text) a caps label", cols: "cols(n, [items], { stretch }) a grid", card: "card({ tag, title, level, body, out, emph, sm })", callout: "callout({ tag, text, emph })",
186
243
  strip: "strip([labels], { on }) numbered chips with arrows", chip: "chip(i, label, state)", phase: "phase(i, label, line) chip then heading", flow: "flow({ label, nodes: [[who, verb, 'a'|'p']], note })",
187
244
  table: "table({ head, rows, emph }) cells: string or { v, meter, cls }", quote: "quote({ text, by, emph })", who: "who(name, role)", stats: "stats([{ v, k, hero }])", delta: "delta(from, to)",
188
- steps: "steps([{ title, when, body, emph }])", list: "list([items])", chapter: "chapter(num) the deck's numeral panel", img: "img(src, cls, alt)", icon: "icon(name) a brand glyph", tile: "tile(name) a glyph on a tile",
245
+ steps: "steps([{ title, when, body, emph }])", list: "list([items])", chapter: "chapter(num) the deck's numeral panel", img: "img(src, cls, alt)", photo: "photo(nameOrPath, { cls, alt, pos }) a photograph, filled and graded", icon: "icon(name) a brand glyph", tile: "tile(name) a glyph on a tile",
189
246
  accent: "accent(text, phrase) the phrase in the accent colour", split: "split(line, sep)", L: "L(n) line n of content.txt", esc: "esc(s)", art: "art(role) a brand art url", asset: "asset(file)",
190
247
  };
191
248
  console.log("");
@@ -209,6 +266,8 @@ try {
209
266
  case "watch": await watch(args[1]); break;
210
267
  case "gallery": process.exit(await gallery());
211
268
  case "poster": process.exit(await poster(args[1]));
269
+ case "social": process.exit(await social(args[1]));
270
+ case "library": process.exit(await library());
212
271
  case "components": await components(); break;
213
272
  case "brands": await brands(); break;
214
273
  default: