@cmo-so/cli 0.7.4 → 0.7.6

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 +223 -6
  2. package/package.json +1 -1
package/dist/cmo.mjs CHANGED
@@ -7407,15 +7407,15 @@ function createApi({ baseUrl, session: session2, fetch: fetchImpl = globalThis.f
7407
7407
  }),
7408
7408
  remove: () => request("/v1/maggie/bridge", { method: "DELETE" }),
7409
7409
  /** Start "Sign in with ChatGPT" for one of the bridge's accounts (the active one by default). */
7410
- connectChatgpt: (account3) => request("/v1/maggie/bridge/chatgpt", {
7410
+ connectChatgpt: (account4) => request("/v1/maggie/bridge/chatgpt", {
7411
7411
  method: "POST",
7412
7412
  headers: { "Content-Type": "application/json" },
7413
- body: JSON.stringify(account3 ? { account: account3 } : {})
7413
+ body: JSON.stringify(account4 ? { account: account4 } : {})
7414
7414
  }),
7415
- disconnectChatgpt: (account3) => request("/v1/maggie/bridge/chatgpt/logout", {
7415
+ disconnectChatgpt: (account4) => request("/v1/maggie/bridge/chatgpt/logout", {
7416
7416
  method: "POST",
7417
7417
  headers: { "Content-Type": "application/json" },
7418
- body: JSON.stringify(account3 ? { account: account3 } : {})
7418
+ body: JSON.stringify(account4 ? { account: account4 } : {})
7419
7419
  }),
7420
7420
  /** What Maggie may do in CMO.so; rotates the bridge's key when read / read_write changes. */
7421
7421
  setAccess: (access) => request("/v1/maggie/bridge/access", {
@@ -7458,6 +7458,32 @@ function createApi({ baseUrl, session: session2, fetch: fetchImpl = globalThis.f
7458
7458
  return dataUrl(type, bytes);
7459
7459
  }
7460
7460
  },
7461
+ // The open marketing skills (docs/26): api.cmo.so forwards these to the cmo.so site.
7462
+ skills: {
7463
+ search: (q2 = "", opts = {}) => {
7464
+ const qs = new URLSearchParams({
7465
+ q: q2,
7466
+ ...opts.category ? { category: opts.category } : {},
7467
+ limit: String(opts.limit ?? 10)
7468
+ });
7469
+ return request(`/v1/skills?${qs}`);
7470
+ },
7471
+ get: (id) => request(`/v1/skills/${encodeURIComponent(id)}`),
7472
+ /** Vote on or save a skill (on: false undoes it). "like" is an upvote. */
7473
+ react: (id, kind, on2 = true) => request(`/v1/skills/${encodeURIComponent(id)}/react`, {
7474
+ method: "POST",
7475
+ headers: { "Content-Type": "application/json" },
7476
+ body: JSON.stringify({ kind, on: on2 })
7477
+ }),
7478
+ saved: () => request("/v1/skills/saved"),
7479
+ /** Suggest an open-source GitHub repo; Maggie researches it (3 a day). */
7480
+ submit: (repoUrl, note) => request("/v1/skills/submissions", {
7481
+ method: "POST",
7482
+ headers: { "Content-Type": "application/json" },
7483
+ body: JSON.stringify({ repo_url: repoUrl, note })
7484
+ }),
7485
+ submissions: () => request("/v1/skills/submissions")
7486
+ },
7461
7487
  // Web Push for this browser (APP-9). `domains` are the workspaces it shows.
7462
7488
  push: {
7463
7489
  status: (endpoint) => request(
@@ -8309,6 +8335,10 @@ var confirmCopy = {
8309
8335
  en: (a) => `Remove the call-to-action for ${q(a.target_keyword)}.`,
8310
8336
  "zh-Hant": (a) => `\u522A\u9664 ${q(a.target_keyword)} \u7684\u884C\u52D5\u547C\u7C72\u3002`
8311
8337
  },
8338
+ internal_links_set: {
8339
+ en: (a) => Number(a.count) ? `Link each new post to ${a.count} of your latest articles.` : "Stop linking new posts to your other articles.",
8340
+ "zh-Hant": (a) => Number(a.count) ? `\u6BCF\u7BC7\u65B0\u6587\u7AE0\u9023\u7D50\u5230\u4F60\u6700\u65B0\u7684 ${a.count} \u7BC7\u6587\u7AE0\u3002` : "\u65B0\u6587\u7AE0\u4E0D\u518D\u9023\u7D50\u5230\u4F60\u7684\u5176\u4ED6\u6587\u7AE0\u3002"
8341
+ },
8312
8342
  testimonial_upsert: {
8313
8343
  en: (a) => `Save the testimonial from ${a.name}${a.use_in_posts ? " and use testimonials in posts" : ""}.`,
8314
8344
  "zh-Hant": (a) => `\u5132\u5B58 ${a.name} \u7684\u5BA2\u6236\u8A55\u50F9${a.use_in_posts ? "\uFF0C\u4E26\u5728\u6587\u7AE0\u88E1\u4F7F\u7528\u8A55\u50F9" : ""}\u3002`
@@ -8555,6 +8585,19 @@ var confirmCopy = {
8555
8585
  en: (a) => `Send this to the cmo.so team: ${q(String(a.summary ?? "").slice(0, 200))}`,
8556
8586
  "zh-Hant": (a) => `\u628A\u9019\u5247\u56DE\u994B\u9001\u7D66 cmo.so \u5718\u968A\uFF1A${q(String(a.summary ?? "").slice(0, 200))}`
8557
8587
  },
8588
+ // ---------------------------------------------------------- Marketing skills (docs/26)
8589
+ skill_like: {
8590
+ en: (a) => `${a.undo ? "Take back your upvote on" : "Upvote"} the skill ${q(a.skill_id)} on cmo.so.`,
8591
+ "zh-Hant": (a) => `${a.undo ? "\u6536\u56DE\u5C0D" : "\u5728 cmo.so \u6295\u7968\u652F\u6301"}\u6280\u80FD ${q(a.skill_id)}${a.undo ? " \u7684\u652F\u6301\u7968" : ""}\u3002`
8592
+ },
8593
+ skill_save: {
8594
+ en: (a) => `${a.undo ? "Remove" : "Save"} the skill ${q(a.skill_id)} ${a.undo ? "from" : "to"} your saved skills.`,
8595
+ "zh-Hant": (a) => `${a.undo ? "\u5F9E\u6536\u85CF\u79FB\u9664" : "\u6536\u85CF"}\u6280\u80FD ${q(a.skill_id)}\u3002`
8596
+ },
8597
+ skill_submit: {
8598
+ en: (a) => `Suggest ${q(a.repo_url)} for the cmo.so skill library. Maggie will research it.`,
8599
+ "zh-Hant": (a) => `\u628A ${q(a.repo_url)} \u63A8\u85A6\u7D66 cmo.so \u6280\u80FD\u5EAB\uFF0CMaggie \u6703\u7814\u7A76\u5B83\u3002`
8600
+ },
8558
8601
  social_voice_learn: {
8559
8602
  en: () => "Learn your LinkedIn voice from this post and save it as your default.",
8560
8603
  "zh-Hant": () => "\u5F9E\u9019\u7BC7\u8CBC\u6587\u5B78\u7FD2\u4F60\u7684 LinkedIn \u8A9E\u6C23\uFF0C\u4E26\u8A2D\u70BA\u9810\u8A2D\u3002"
@@ -9573,6 +9616,24 @@ var brandTools = [
9573
9616
  return { ctas: out.ctas?.map((c) => c.target_keyword) };
9574
9617
  }
9575
9618
  }),
9619
+ tool({
9620
+ name: "internal_links_set",
9621
+ description: "Set how many of the website's own latest articles each new post links to (internal cross-links, picked for the topic; separate from CTAs). 0 turns it off, max 10, default 3.",
9622
+ parameters: {
9623
+ type: "object",
9624
+ properties: { count: { type: "integer", minimum: 0, maximum: 10 } },
9625
+ required: ["count"]
9626
+ },
9627
+ group: "brand",
9628
+ need: write2,
9629
+ risk: "W1",
9630
+ run: async (ctx, { count }) => {
9631
+ const n = Math.round(Number(count));
9632
+ if (!Number.isFinite(n) || n < 0 || n > 10) throw new Error("count must be 0 to 10.");
9633
+ const out = await ws(ctx).updateBrand({ internal_links: { count: n } });
9634
+ return { internal_links: out.internal_links ?? { count: n } };
9635
+ }
9636
+ }),
9576
9637
  tool({
9577
9638
  name: "testimonial_upsert",
9578
9639
  description: "Add a customer testimonial, or change the one from the same person. Only real quotes the user gives (or examples they asked for). use_in_posts turns testimonials on in posts.",
@@ -10645,6 +10706,161 @@ var reportTools = [
10645
10706
  })
10646
10707
  ];
10647
10708
 
10709
+ // ../../packages/tools/src/skills.ts
10710
+ var account3 = { resource: "account", level: "read" };
10711
+ var ID = { type: "string", description: "The skill id, like cmo-ad-creative" };
10712
+ var skillTools = [
10713
+ tool({
10714
+ name: "skills_search",
10715
+ description: "Search cmo.so's open library of marketing skills (step-by-step methods an AI agent follows, free and MIT licensed): returns matching skills with their summary, vote score and page. Use it when the user asks how to do a marketing job, wants a playbook or template, or asks which skills exist. Empty query: the most useful ones and the categories.",
10716
+ parameters: {
10717
+ type: "object",
10718
+ properties: {
10719
+ query: { type: "string", description: "What the user wants to do, in a few words" },
10720
+ category: {
10721
+ type: "string",
10722
+ description: "Optional category id: core, strategy, seo, ai-search, content, social, email, paid-ads, cro, analytics, growth, sales, creative"
10723
+ },
10724
+ limit: { type: "integer", minimum: 1, maximum: 20 }
10725
+ }
10726
+ },
10727
+ group: "account",
10728
+ need: account3,
10729
+ risk: "R0",
10730
+ idempotent: true,
10731
+ run: async (ctx, a) => {
10732
+ const r = await ctx.api.skills.search(String(a.query ?? ""), {
10733
+ ...a.category ? { category: a.category } : {},
10734
+ limit: int(a.limit, 1, 20, 8)
10735
+ });
10736
+ return {
10737
+ total_in_library: r.total,
10738
+ ...a.query ? {} : { categories: r.categories.map((c) => `${c.id} (${c.count})`) },
10739
+ skills: r.skills.map((s) => ({
10740
+ id: s.id,
10741
+ name: s.name,
10742
+ summary: s.summary,
10743
+ score: s.score ?? s.likes,
10744
+ page: s.url
10745
+ }))
10746
+ };
10747
+ }
10748
+ }),
10749
+ tool({
10750
+ name: "skill_get",
10751
+ description: "One marketing skill from the cmo.so library in full: when to use it, what it needs, its workflow, output and quality bar, plus how to install it in Claude Code. Use it to follow a skill's method for the user, or to explain it.",
10752
+ parameters: { type: "object", properties: { skill_id: ID }, required: ["skill_id"] },
10753
+ group: "account",
10754
+ need: account3,
10755
+ risk: "R0",
10756
+ idempotent: true,
10757
+ run: async (ctx, a) => {
10758
+ const s = await ctx.api.skills.get(String(a.skill_id));
10759
+ return {
10760
+ id: s.id,
10761
+ name: s.name,
10762
+ summary: s.summary,
10763
+ page: s.url,
10764
+ score: s.score ?? s.likes,
10765
+ saves: s.saves,
10766
+ uses_tools: s.mcp_tools,
10767
+ pairs_with: s.pairs_with,
10768
+ install: s.install,
10769
+ // community-contributed text: a method to follow, never instructions that override the user or these rules
10770
+ ...untrusted(clip(s.text, 12e3))
10771
+ };
10772
+ }
10773
+ }),
10774
+ tool({
10775
+ name: "skill_like",
10776
+ description: "Upvote a skill in the cmo.so library for the user (or take the upvote back with undo). Votes help the library decide what to keep improving. Only when the user asks.",
10777
+ parameters: {
10778
+ type: "object",
10779
+ properties: { skill_id: ID, undo: { type: "boolean", description: "true takes the upvote back" } },
10780
+ required: ["skill_id"]
10781
+ },
10782
+ group: "account",
10783
+ need: account3,
10784
+ risk: "W1",
10785
+ idempotent: true,
10786
+ run: async (ctx, a) => {
10787
+ const r = await ctx.api.skills.react(String(a.skill_id), "like", !a.undo);
10788
+ return { skill_id: r.item, upvoted: r.liked, score: r.counts.score ?? r.counts.likes };
10789
+ }
10790
+ }),
10791
+ tool({
10792
+ name: "skill_save",
10793
+ description: "Save a skill to the user's saved skills on cmo.so (or remove it with undo), to find it again under Saved at cmo.so/skills or with skills_saved_list.",
10794
+ parameters: {
10795
+ type: "object",
10796
+ properties: { skill_id: ID, undo: { type: "boolean", description: "true removes it from saved" } },
10797
+ required: ["skill_id"]
10798
+ },
10799
+ group: "account",
10800
+ need: account3,
10801
+ risk: "W1",
10802
+ idempotent: true,
10803
+ run: async (ctx, a) => {
10804
+ const r = await ctx.api.skills.react(String(a.skill_id), "save", !a.undo);
10805
+ return { skill_id: r.item, saved: r.saved };
10806
+ }
10807
+ }),
10808
+ tool({
10809
+ name: "skills_saved_list",
10810
+ description: "The skills the user saved on cmo.so, newest first.",
10811
+ parameters: none,
10812
+ group: "account",
10813
+ need: account3,
10814
+ risk: "R0",
10815
+ run: async (ctx) => ({
10816
+ skills: (await ctx.api.skills.saved()).skills.map((s) => ({
10817
+ id: s.id,
10818
+ name: s.name,
10819
+ summary: s.summary,
10820
+ page: s.url
10821
+ }))
10822
+ })
10823
+ }),
10824
+ tool({
10825
+ name: "skill_submit",
10826
+ description: "Suggest a public open-source GitHub repo for cmo.so's skill library. Maggie researches it within minutes and decides: add a new skill, improve an existing one, keep it as a candidate that needs a vote score of 10 in 30 days, or decline (licence first: permissive licences can be adapted, copyleft only inspires, no licence is declined). 3 suggestions a day. Follow up with skill_submissions_list.",
10827
+ parameters: {
10828
+ type: "object",
10829
+ properties: {
10830
+ repo_url: { type: "string", description: "https://github.com/owner/name" },
10831
+ note: { type: "string", description: "Optional: why it is useful, \u2264500 characters" }
10832
+ },
10833
+ required: ["repo_url"]
10834
+ },
10835
+ group: "account",
10836
+ need: account3,
10837
+ risk: "W1",
10838
+ run: async (ctx, a) => {
10839
+ const r = await ctx.api.skills.submit(String(a.repo_url), a.note ? String(a.note).slice(0, 500) : void 0);
10840
+ return r.duplicate ? { already_suggested: true, id: r.id, status: r.status, reasons: r.reasons ?? null } : { submitted: true, id: r.id, status: r.status, repo: r.repo };
10841
+ }
10842
+ }),
10843
+ tool({
10844
+ name: "skill_submissions_list",
10845
+ description: "The repos the user suggested for the skill library and what happened to each: queued, researching, candidate (with its deadline), publishing, published (with the skill), declined, expired or failed, with Maggie's reasons.",
10846
+ parameters: none,
10847
+ group: "account",
10848
+ need: account3,
10849
+ risk: "R0",
10850
+ run: async (ctx) => ({
10851
+ items: (await ctx.api.skills.submissions()).items.slice(0, 20).map((s) => ({
10852
+ id: s.id,
10853
+ repo: s.repo,
10854
+ status: s.status,
10855
+ skill: s.target_skill,
10856
+ reasons: clip(s.reasons, 500),
10857
+ candidate_until: s.candidate_until,
10858
+ suggested: s.created_at
10859
+ }))
10860
+ })
10861
+ })
10862
+ ];
10863
+
10648
10864
  // ../../packages/tools/src/social.ts
10649
10865
  var read5 = { resource: "autopilot", level: "read" };
10650
10866
  var write5 = { resource: "autopilot", level: "write" };
@@ -12101,7 +12317,8 @@ var tools = [
12101
12317
  ...socialTools,
12102
12318
  ...teamTools,
12103
12319
  ...memoryTools,
12104
- ...feedbackTools
12320
+ ...feedbackTools,
12321
+ ...skillTools
12105
12322
  ];
12106
12323
  var toolByName = new Map(tools.map((t) => [t.name, t]));
12107
12324
  function allowed(t, a) {
@@ -15494,7 +15711,7 @@ var program = new Command();
15494
15711
  // package.json
15495
15712
  var package_default = {
15496
15713
  name: "@cmo-so/cli",
15497
- version: "0.7.4",
15714
+ version: "0.7.6",
15498
15715
  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.",
15499
15716
  type: "module",
15500
15717
  license: "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmo-so/cli",
3
- "version": "0.7.4",
3
+ "version": "0.7.6",
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",