@cmo-so/cli 0.12.8 → 0.12.10

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 (2) hide show
  1. package/dist/cmo.mjs +200 -21
  2. package/package.json +1 -1
package/dist/cmo.mjs CHANGED
@@ -14658,6 +14658,54 @@ function replace(blocks, ref, next, synced = {}) {
14658
14658
  function sectionAt(blocks, ref, synced = {}) {
14659
14659
  return pick(locate(blocks), ref, synced);
14660
14660
  }
14661
+ function widthValue(width) {
14662
+ const w = String(width ?? "").trim();
14663
+ if (w === "content") return null;
14664
+ if (w === "wide") return "var(--wp--style--global--wide-size)";
14665
+ if (/^\d{2,4}(\.\d+)?(px|rem|em|vw|%)$/.test(w)) return w;
14666
+ throw new Error('width: "content", "wide" or a CSS length such as 1240px.');
14667
+ }
14668
+ var NARROW_PX = 700;
14669
+ var sizePx = (v) => {
14670
+ const m = /^(\d+(?:\.\d+)?)(px|rem|em)$/.exec(v);
14671
+ return m ? Number(m[1]) * (m[2] === "px" ? 1 : 16) : null;
14672
+ };
14673
+ function setSectionWidth(b, width) {
14674
+ const size = widthValue(width);
14675
+ const before = [];
14676
+ const visit = (x, top) => {
14677
+ let changed = false;
14678
+ for (const c of x.inner) if (visit(c, false)) changed = true;
14679
+ const lay = x.attrs.layout;
14680
+ const constrained = lay?.type === "constrained";
14681
+ const cur = lay?.contentSize;
14682
+ const px = cur ? sizePx(cur) : null;
14683
+ const eligible = constrained && (!cur || cur.includes("--wp--style--global") || px == null || px >= NARROW_PX);
14684
+ if (eligible || top && !lay && (x.name === "core/group" || x.name === "core/cover")) {
14685
+ if ((cur ?? null) !== size) {
14686
+ before.push(cur ?? "content");
14687
+ const next = { ...lay ?? {}, type: "constrained" };
14688
+ if (size) next.contentSize = size;
14689
+ else delete next.contentSize;
14690
+ x.attrs = { ...x.attrs, layout: next };
14691
+ changed = true;
14692
+ }
14693
+ }
14694
+ if (changed) delete x.raw;
14695
+ return changed;
14696
+ };
14697
+ visit(b, true);
14698
+ return { containers: before.length, before };
14699
+ }
14700
+ function refreshRaw(list3) {
14701
+ let any = false;
14702
+ for (const b of list3) {
14703
+ const inner = refreshRaw(b.inner);
14704
+ if (inner) delete b.raw;
14705
+ if (inner || b.raw == null) any = true;
14706
+ }
14707
+ return any;
14708
+ }
14661
14709
  var escRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14662
14710
  function applyEdits(b, e) {
14663
14711
  let m = serializeBlock(b);
@@ -15195,6 +15243,15 @@ async function styleGet(site) {
15195
15243
  fonts: (user.settings?.typography?.fontFamilies?.theme ?? []).map((f) => f.slug)
15196
15244
  },
15197
15245
  contrast: contrastReport(Object.fromEntries([...palette.values()].map((c) => [c.slug, c.color]))),
15246
+ layout: {
15247
+ content: user.settings?.layout?.contentSize ?? base.settings?.layout?.contentSize ?? null,
15248
+ wide: user.settings?.layout?.wideSize ?? base.settings?.layout?.wideSize ?? null,
15249
+ padding: {
15250
+ left: user.styles?.spacing?.padding?.left ?? base.styles?.spacing?.padding?.left ?? null,
15251
+ right: user.styles?.spacing?.padding?.right ?? base.styles?.spacing?.padding?.right ?? null
15252
+ }
15253
+ },
15254
+ css: user.styles?.css ?? "",
15198
15255
  _palette: [...palette.values()],
15199
15256
  _fonts: [...fonts.values()],
15200
15257
  _user: user
@@ -15235,6 +15292,17 @@ function contrastReport(colors) {
15235
15292
  });
15236
15293
  }
15237
15294
  async function styleSet(site, change, force = false) {
15295
+ const len = (v, what) => {
15296
+ const t = String(v ?? "").trim();
15297
+ if (!/^(0|\d{1,4}(\.\d+)?(px|rem|em|vw|%)|var\(--wp--preset--spacing--[a-z0-9-]+\)|clamp\([0-9a-z.,%+\-\s()]+\))$/i.test(
15298
+ t
15299
+ ))
15300
+ throw new Error(`${what}: "${t}" isn't a CSS length like 1240px, 2rem or var(--wp--preset--spacing--50).`);
15301
+ return t;
15302
+ };
15303
+ for (const css of [change.css, change.css_append])
15304
+ if (css != null && /<\/?style|<\?|<script|@import\s+url\(\s*['"]?(?!https:)/i.test(css))
15305
+ throw new Error("css: plain CSS only (no <style>, <script>, PHP or non-https imports).");
15238
15306
  const cur = await styleGet(site);
15239
15307
  const palette = cur._palette.map((c) => ({ ...c }));
15240
15308
  for (const [slug, color] of Object.entries(change.colors ?? {})) {
@@ -15259,15 +15327,48 @@ async function styleSet(site, change, force = false) {
15259
15327
  );
15260
15328
  const user = cur._user;
15261
15329
  const settings2 = { ...user.settings ?? {} };
15330
+ const styles = { ...user.styles ?? {} };
15262
15331
  if (change.colors) settings2.color = { ...settings2.color ?? {}, palette: { theme: palette } };
15263
15332
  if (change.fonts)
15264
15333
  settings2.typography = { ...settings2.typography ?? {}, fontFamilies: { theme: fonts } };
15334
+ if (change.layout) {
15335
+ const layout = { ...settings2.layout ?? {} };
15336
+ if (change.layout.content != null) layout.contentSize = len(change.layout.content, "layout.content");
15337
+ if (change.layout.wide != null) layout.wideSize = len(change.layout.wide, "layout.wide");
15338
+ settings2.layout = layout;
15339
+ }
15340
+ if (change.padding) {
15341
+ const spacing = { ...styles.spacing ?? {} };
15342
+ const pad = { ...spacing.padding ?? {} };
15343
+ if (change.padding.left != null) pad.left = len(change.padding.left, "padding.left");
15344
+ if (change.padding.right != null) pad.right = len(change.padding.right, "padding.right");
15345
+ styles.spacing = { ...spacing, padding: pad };
15346
+ settings2.useRootPaddingAwareAlignments = true;
15347
+ }
15348
+ if (change.css != null) styles.css = change.css;
15349
+ if (change.css_append)
15350
+ styles.css = `${String(styles.css ?? "").trimEnd()}
15351
+
15352
+ ${change.css_append.trim()}
15353
+ `.trimStart();
15265
15354
  await call(site, {
15266
15355
  method: "POST",
15267
15356
  path: `/wp/v2/global-styles/${cur.id}`,
15268
- body: { settings: settings2, styles: user.styles ?? {} }
15357
+ body: { settings: settings2, styles }
15269
15358
  });
15359
+ const changedLayout = change.layout || change.padding;
15270
15360
  return {
15361
+ ...changedLayout ? {
15362
+ layout: {
15363
+ before: cur.layout,
15364
+ after: {
15365
+ content: settings2.layout?.contentSize ?? cur.layout.content,
15366
+ wide: settings2.layout?.wideSize ?? cur.layout.wide,
15367
+ padding: change.padding ? { ...cur.layout.padding, ...change.padding } : cur.layout.padding
15368
+ }
15369
+ }
15370
+ } : {},
15371
+ ...change.css != null || change.css_append ? { css: { before: cur.css.length, after: String(styles.css ?? "").length, before_text: cur.css } } : {},
15271
15372
  colors: Object.fromEntries(
15272
15373
  Object.entries(change.colors ?? {}).map(([k, v]) => [k, { before: cur.colors[k], after: v }])
15273
15374
  ),
@@ -16386,7 +16487,7 @@ var siteTools = [
16386
16487
  }),
16387
16488
  tool({
16388
16489
  name: "wp_style_get",
16389
- description: "The brand: colours (by name: canvas, panel, soft, ink, muted, olive, accent\u2026) and fonts (display, body), and the text contrast of each colour pair.",
16490
+ description: "The site's look: colours (by name: canvas, panel, soft, ink, muted, olive, accent\u2026), fonts (display, body), layout widths (content, wide) and page padding, the extra CSS, and the text contrast of each colour pair.",
16390
16491
  parameters: { type: "object", properties: {} },
16391
16492
  group: "site",
16392
16493
  need: READ,
@@ -16394,7 +16495,14 @@ var siteTools = [
16394
16495
  gates: ["WP"],
16395
16496
  run: async (ctx) => {
16396
16497
  const s = await styleGet(siteOf(ctx));
16397
- return { colors: s.colors, fonts: s.fonts, customized: s.customized, contrast: s.contrast };
16498
+ return {
16499
+ colors: s.colors,
16500
+ fonts: s.fonts,
16501
+ layout: s.layout,
16502
+ css: s.css,
16503
+ customized: s.customized,
16504
+ contrast: s.contrast
16505
+ };
16398
16506
  }
16399
16507
  })
16400
16508
  ];
@@ -16836,12 +16944,15 @@ var sectionTools = [
16836
16944
  }),
16837
16945
  tool({
16838
16946
  name: "wp_section_update",
16839
- description: "Change one section. edits: change text, links or pictures inside it and keep its design (works on any section, the homepage too). slots: write the section again from new content (generated types; give the complete content). faq: add, remove (1-based numbers) or replace questions.",
16947
+ description: 'Change one section. width: line its content up with the header and footer ("wide") or another width, one section or all. edits: change text, links or pictures inside it and keep its design (works on any section, the homepage too). slots: write the section again from new content (generated types; give the complete content). faq: add, remove (1-based numbers) or replace questions.',
16840
16948
  parameters: {
16841
16949
  type: "object",
16842
16950
  properties: {
16843
16951
  page: PAGE,
16844
- section: str2("Section id from wp_page_get (s2, m4) or its type (faq)."),
16952
+ section: str2('Section id from wp_page_get (s2, m4) or its type (faq); "all" with width only.'),
16953
+ width: str2(
16954
+ `The section's inner width: "content" (the site's content width), "wide" (the site's wide width, as the header and footer) or a CSS length (1240px). Changes the section and its containers wider than 700px; with section "all", every section on the page.`
16955
+ ),
16845
16956
  edits: EDITS,
16846
16957
  slots: obj("Complete new content for the section (same type unless type is given)."),
16847
16958
  markup: str2(
@@ -16870,6 +16981,19 @@ var sectionTools = [
16870
16981
  ),
16871
16982
  run: (ctx, a) => changePage(ctx, a, (blocks, kit) => {
16872
16983
  const ref = String(a.section);
16984
+ if (a.width != null && a.width !== "") {
16985
+ const targets = ref === "all" ? locate(blocks) : [sectionAt(blocks, ref, kit.syncedTitles)];
16986
+ const changed = {};
16987
+ for (const t of targets) {
16988
+ if (t.block.name === "core/block") continue;
16989
+ const r = setSectionWidth(t.block, a.width);
16990
+ if (r.containers) changed[t.id] = r;
16991
+ }
16992
+ refreshRaw(blocks);
16993
+ if (!Object.keys(changed).length) throw new Error("Those sections already have that width.");
16994
+ return { blocks, note: { width: String(a.width), sections: changed } };
16995
+ }
16996
+ if (ref === "all") throw new Error('section "all" works with width only.');
16873
16997
  const target = sectionAt(blocks, ref, kit.syncedTitles);
16874
16998
  const type = classify(target.block, target.region, kit.syncedTitles);
16875
16999
  let next;
@@ -17236,7 +17360,7 @@ var childTools = [
17236
17360
  }),
17237
17361
  tool({
17238
17362
  name: "wp_child_theme_update",
17239
- description: "Change a Maggie child theme's files (default: the active child): brand colours (by name, hex) and fonts (by name, CSS font-family of fonts already in it) in theme.json, extra theme.json styles, plain CSS (replace or append), the header or footer (block markup), templates, and the site's own patterns (shown in the editor's inserter). Data only, never code. Each change keeps a backup (wp_child_theme_restore). Changes made with wp_style_set / wp_header_set are stored in the database and win over the child's files.",
17363
+ description: "Change a Maggie child theme's files (default: the active child): brand colours (by name, hex) and fonts (by name, CSS font-family of fonts already in it) in theme.json, extra theme.json styles, plain CSS (replace or append), the site's own JavaScript (assets/js/site.js, replace or append; runs for every visitor), the header or footer (block markup), templates, and the site's own patterns (shown in the editor's inserter). No PHP or server code. Each change keeps a backup (wp_child_theme_restore). Changes made with wp_style_set / wp_header_set are stored in the database and win over the child's files.",
17240
17364
  parameters: {
17241
17365
  type: "object",
17242
17366
  properties: {
@@ -17257,6 +17381,10 @@ var childTools = [
17257
17381
  styles: obj('theme.json styles to merge in (e.g. { "elements": { "button": { \u2026 } } }).'),
17258
17382
  css: str2("Replace the child's CSS (plain CSS)."),
17259
17383
  css_append: str2("Add CSS at the end."),
17384
+ js: str2(
17385
+ `Replace the site's own script (assets/js/site.js, plain JavaScript loaded deferred on every page; "" turns it off). Needs Maggie Core 0.12+ and a login allowed to add scripts. No eval or new Function; addresses only on the site or cdn.jsdelivr.net, unpkg.com, cdnjs.cloudflare.com.`
17386
+ ),
17387
+ js_append: str2("Add JavaScript at the end of the site's script."),
17260
17388
  header: str2("Header block markup (parts/header.html)."),
17261
17389
  footer: str2("Footer block markup (parts/footer.html)."),
17262
17390
  templates: obj('Templates by slug \u2192 block markup, e.g. { "page-landing": "\u2026" }.'),
@@ -17270,8 +17398,8 @@ var childTools = [
17270
17398
  gates: [...GATES],
17271
17399
  confirm: (a, lang) => say(
17272
17400
  lang,
17273
- `Change the child theme ${q2(a.slug, "(the active one)")} on your website.`,
17274
- `\u4FEE\u6539\u7DB2\u7AD9\u7684\u5B50\u4E3B\u984C ${q2(a.slug, "\uFF08\u76EE\u524D\u4F7F\u7528\u4E2D\u7684\uFF09")}\u3002`
17401
+ `Change the child theme ${q2(a.slug, "(the active one)")} on your website.${typeof a.js === "string" || typeof a.js_append === "string" ? a.js === "" ? " Turns off the site's own script." : " Includes JavaScript that runs in every visitor's browser on every page." : ""}`,
17402
+ `\u4FEE\u6539\u7DB2\u7AD9\u7684\u5B50\u4E3B\u984C ${q2(a.slug, "\uFF08\u76EE\u524D\u4F7F\u7528\u4E2D\u7684\uFF09")}\u3002${typeof a.js === "string" || typeof a.js_append === "string" ? a.js === "" ? "\u95DC\u9589\u7DB2\u7AD9\u81EA\u8A02\u8173\u672C\u3002" : "\u5305\u542B\u6703\u5728\u6BCF\u4F4D\u8A2A\u5BA2\u700F\u89BD\u5668\u3001\u6BCF\u500B\u9801\u9762\u57F7\u884C\u7684 JavaScript\u3002" : ""}`
17275
17403
  ),
17276
17404
  run: async (ctx, a) => {
17277
17405
  const site = siteOf(ctx);
@@ -17369,6 +17497,22 @@ var childTools = [
17369
17497
  if (typeof a.css_append === "string")
17370
17498
  files["style.css"] = `${(files["style.css"] ?? cur["style.css"] ?? "").replace(/\n*$/, "\n")}${a.css_append}
17371
17499
  `;
17500
+ if (typeof a.js === "string" || typeof a.js_append === "string") {
17501
+ const parentV = String(info.parent?.version ?? "0");
17502
+ const [maj, min] = parentV.split(".").map(Number);
17503
+ if ((maj ?? 0) === 0 && (min ?? 0) < 12)
17504
+ throw new Error(
17505
+ `The site's script needs Maggie Core 0.12 or later (the site has ${parentV}): update it with wp_theme_update first.`
17506
+ );
17507
+ const js = typeof a.js === "string" ? a.js : `${(cur["assets/js/site.js"] ?? "").replace(/\n*$/, "\n")}${String(a.js_append)}
17508
+ `.trimStart();
17509
+ if (/<\/?script\b/i.test(js)) throw new Error("js: plain JavaScript only, no <script> tags.");
17510
+ if (/<\?(php|=)?/i.test(js)) throw new Error("js: PHP isn't allowed.");
17511
+ files["assets/js/site.js"] = js;
17512
+ notes.push(
17513
+ js.trim() ? "assets/js/site.js runs deferred on every page; undo with wp_child_theme_restore." : "The site's own script is off."
17514
+ );
17515
+ }
17372
17516
  const markup = (path3, m) => {
17373
17517
  if (typeof m !== "string" || !m.trim()) return;
17374
17518
  if (!parse(m).some((b) => b.name)) throw new Error(`${path3}: give block markup (<!-- wp:\u2026 -->).`);
@@ -17775,23 +17919,41 @@ var elementTools = [
17775
17919
  }),
17776
17920
  tool({
17777
17921
  name: "wp_style_set",
17778
- description: "Change brand colours (by name, hex) and fonts (by name, CSS font-family) for the whole site. Refuses colours that would make text hard to read (contrast under 4.5:1).",
17922
+ description: "Change the whole site's look: brand colours (by name, hex), fonts (by name, CSS font-family), layout widths (content: the width sections and text use; wide: the header, footer and wide blocks), the left and right page padding, and extra CSS. Saved in the site's Global Styles (works on any block theme; returns before and after). Refuses colours that would make text hard to read (contrast under 4.5:1). One section's width: wp_section_update width.",
17779
17923
  parameters: {
17780
17924
  type: "object",
17781
17925
  properties: {
17782
17926
  colors: obj('e.g. { "olive": "#4F5B48", "accent": "#A8834F" }.'),
17783
- fonts: obj('e.g. { "display": "\\"Playfair Display\\", Georgia, serif" }.')
17927
+ fonts: obj('e.g. { "display": "\\"Playfair Display\\", Georgia, serif" }.'),
17928
+ layout: obj('Widths, e.g. { "content": "1240px", "wide": "1240px" }.', {
17929
+ content: str2("Content width (CSS length)."),
17930
+ wide: str2("Wide width (CSS length).")
17931
+ }),
17932
+ padding: obj('Left and right page padding, e.g. { "left": "24px", "right": "24px" }.', {
17933
+ left: str2(""),
17934
+ right: str2("")
17935
+ }),
17936
+ css: str2("Replace the site's extra CSS (Global Styles \u2192 Additional CSS); plain CSS only."),
17937
+ css_append: str2("Add this CSS after the site's extra CSS.")
17784
17938
  }
17785
17939
  },
17786
17940
  group: "site",
17787
17941
  need: WRITE,
17788
17942
  risk: "W3",
17789
17943
  gates: [...GATES],
17790
- confirm: (_a3, lang) => say(lang, "Change the website's brand colours or fonts (every page).", "\u4FEE\u6539\u7DB2\u7AD9\u7684\u54C1\u724C\u984F\u8272\u6216\u5B57\u9AD4\uFF08\u6240\u6709\u9801\u9762\uFF09\u3002"),
17791
- run: async (ctx, a) => {
17792
- const r = await styleSet(siteOf(ctx), { colors: a.colors, fonts: a.fonts });
17793
- return r;
17794
- }
17944
+ confirm: (_a3, lang) => say(
17945
+ lang,
17946
+ "Change the website's look for every page (colours, fonts, widths, padding or extra CSS); the result shows before and after.",
17947
+ "\u4FEE\u6539\u7DB2\u7AD9\u6240\u6709\u9801\u9762\u7684\u5916\u89C0\uFF08\u984F\u8272\u3001\u5B57\u9AD4\u3001\u5BEC\u5EA6\u3001\u908A\u8DDD\u6216\u984D\u5916 CSS\uFF09\uFF1B\u7D50\u679C\u6703\u5217\u51FA\u4FEE\u6539\u524D\u5F8C\u3002"
17948
+ ),
17949
+ run: async (ctx, a) => styleSet(siteOf(ctx), {
17950
+ colors: a.colors,
17951
+ fonts: a.fonts,
17952
+ layout: a.layout,
17953
+ padding: a.padding,
17954
+ css: typeof a.css === "string" ? a.css : void 0,
17955
+ css_append: typeof a.css_append === "string" ? a.css_append : void 0
17956
+ })
17795
17957
  })
17796
17958
  ];
17797
17959
  function checkMarkup(markup, what) {
@@ -21766,7 +21928,7 @@ var program = new Command();
21766
21928
  // package.json
21767
21929
  var package_default = {
21768
21930
  name: "@cmo-so/cli",
21769
- version: "0.12.8",
21931
+ version: "0.12.10",
21770
21932
  description: "CMO.so on the command line: your websites, ideas, blog posts, LinkedIn queue and results, plus a local MCP server for Claude Desktop, Cursor and other MCP clients.",
21771
21933
  type: "module",
21772
21934
  license: "MIT",
@@ -32813,6 +32975,16 @@ function registerWp(program3) {
32813
32975
  o.preview ? { tool: "wp_section_update", args } : args
32814
32976
  );
32815
32977
  });
32978
+ section.command("width <page> <section> <width>").description(
32979
+ 'line sections up: width "wide" (as the header and footer), "content" or 1240px; section "all" = every section'
32980
+ ).option("--preview", "show the result, save nothing").action(async (p, s, w, o, c) => {
32981
+ const args = { page: p, section: s, width: w };
32982
+ await run(
32983
+ g(c),
32984
+ o.preview ? "wp_preview" : "wp_section_update",
32985
+ o.preview ? { tool: "wp_section_update", args } : args
32986
+ );
32987
+ });
32816
32988
  pos(section.command("move <page> <section>").description("move a section")).action(
32817
32989
  (p, s, o, c) => run(g(c), "wp_section_move", { page: p, section: s, ...posArgs(o) })
32818
32990
  );
@@ -32878,12 +33050,16 @@ ${table(r.issues, ["severity", "code", "path", "detail"])}`;
32878
33050
  await run(g(c), part === "header" ? "wp_header_set" : "wp_footer_set", args);
32879
33051
  });
32880
33052
  }
32881
- const style = wp.command("style").description("brand colours and fonts");
33053
+ const style = wp.command("style").description("brand colours, fonts, layout widths, page padding and extra CSS");
32882
33054
  style.command("get", { isDefault: true }).action((_o, c) => run(g(c), "wp_style_get", {}));
32883
- style.command("set [colors...]").description(`e.g. cmo wp style set olive=#4F5B48 --font 'display="Playfair Display", serif'`).option("--font <slug=family...>").action(
32884
- (colors, o, c) => run(g(c), "wp_style_set", {
33055
+ style.command("set [colors...]").description(`e.g. cmo wp style set olive=#4F5B48 --font 'display="Playfair Display", serif'`).option("--font <slug=family...>").option("--content <width>", "content width, e.g. 1240px").option("--wide <width>", "wide width (header, footer, wide blocks)").option("--padding <length>", "left and right page padding, e.g. 24px").option("--css-file <file>", "replace the site's extra CSS with this file").option("--css-append <file>", "add this CSS file after the site's extra CSS").action(
33056
+ async (colors, o, c) => run(g(c), "wp_style_set", {
32885
33057
  colors: colors.length ? Object.fromEntries(pairs(colors)) : void 0,
32886
- fonts: o.font ? Object.fromEntries(pairs(o.font)) : void 0
33058
+ fonts: o.font ? Object.fromEntries(pairs(o.font)) : void 0,
33059
+ layout: o.content || o.wide ? { content: o.content, wide: o.wide } : void 0,
33060
+ padding: o.padding ? { left: o.padding, right: o.padding } : void 0,
33061
+ css: o.cssFile ? await readFile2(o.cssFile, "utf8") : void 0,
33062
+ css_append: o.cssAppend ? await readFile2(o.cssAppend, "utf8") : void 0
32887
33063
  })
32888
33064
  );
32889
33065
  const media = wp.command("media").description("pictures in the media library");
@@ -33000,12 +33176,15 @@ ${table(r.issues, ["severity", "code", "path", "detail"])}`;
33000
33176
  ).option(
33001
33177
  "--from <file>",
33002
33178
  "JSON: { colors, fonts, styles, css, css_append, header, footer, templates, patterns, remove_patterns }"
33003
- ).option("--color <slug=hex...>").option("--css-file <file>", "replace the CSS with this file").option("--header-file <file>", "header block markup").option("--footer-file <file>", "footer block markup").action(
33179
+ ).option("--color <slug=hex...>").option("--css-file <file>", "replace the CSS with this file").option("--js-file <file>", "replace the site's script (assets/js/site.js, runs on every page) with this file").option("--js-append <file>", "add this JavaScript file at the end of the site's script").option("--js-off", "turn the site's script off").option("--header-file <file>", "header block markup").option("--footer-file <file>", "footer block markup").action(
33004
33180
  async (slug, o, c) => {
33005
33181
  const args = o.from ? await json(o.from) : {};
33006
33182
  if (slug) args.slug = slug;
33007
33183
  if (o.color) args.colors = Object.fromEntries(pairs(o.color));
33008
33184
  if (o.cssFile) args.css = await readFile2(o.cssFile, "utf8");
33185
+ if (o.jsFile) args.js = await readFile2(o.jsFile, "utf8");
33186
+ if (o.jsAppend) args.js_append = await readFile2(o.jsAppend, "utf8");
33187
+ if (o.jsOff) args.js = "";
33009
33188
  if (o.headerFile) args.header = await readFile2(o.headerFile, "utf8");
33010
33189
  if (o.footerFile) args.footer = await readFile2(o.footerFile, "utf8");
33011
33190
  await run(g(c), "wp_child_theme_update", args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmo-so/cli",
3
- "version": "0.12.8",
3
+ "version": "0.12.10",
4
4
  "description": "CMO.so on the command line: your websites, ideas, blog posts, LinkedIn queue and results, plus a local MCP server for Claude Desktop, Cursor and other MCP clients.",
5
5
  "type": "module",
6
6
  "license": "MIT",