@letterstory/cli 0.176.81 → 0.176.83

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.
package/lib/cli.mjs CHANGED
@@ -85,7 +85,7 @@ import {
85
85
  import { fetchParkedSections, withoutParked } from "./parked.mjs";
86
86
 
87
87
  // Matches cli/package.json; publish-cli.yml rewrites both to the published version.
88
- export const VERSION = "0.176.81";
88
+ export const VERSION = "0.176.83";
89
89
 
90
90
  /** The exit code for a gate refusal (CARD_REQUIRED, PAYMENT_FAILED, OUT_OF_TOKENS): 1 is a plain error. */
91
91
  export const EXIT_PAYMENT_REQUIRED = 3;
@@ -119,12 +119,10 @@ export const BOOLEAN_FLAGS = new Set([
119
119
  "keep-key",
120
120
  "delete-key",
121
121
  "keep",
122
- // automate add/set: on or off; only identified visitors marked hot leads; run the prompt in a Build sandbox.
122
+ // automate add/set: on or off; only identified visitors marked hot leads.
123
123
  "on",
124
124
  "off",
125
125
  "hot-leads-only",
126
- "sandbox",
127
- "no-sandbox",
128
126
  // admin crm prospect-org: start (or retry) the org prepared for the booked call.
129
127
  "start",
130
128
  // site research-builds: run now even when the stored pass is under an hour old.
@@ -566,6 +564,7 @@ Collections (the content engine as a primitive — one door, 'collection run'):
566
564
  collection cancel <run-id>
567
565
  collection inventory <id> Where every piece is; each finished draft's publish-on-its-own time
568
566
  collection links <id> [--refresh] Internal-link refresh: on/off, posts due (--refresh runs the next batch now)
567
+ collection mentions <id> --apply Apply the add-the-mention suggestions past their review window
569
568
  collection delete <id> --yes
570
569
 
571
570
  Phantomstory (a collection Letter hosts as an independent publication):
@@ -656,25 +655,29 @@ Reviewers:
656
655
  and what Letterpress then does
657
656
  automate show <automation-id>
658
657
  automate add [--template post_new_issues|recommended_actions|competitor_pages|competitor_takes|
659
- competitor_changes|icp_visitors|vc_visitors|plan_updates|weekly_release_notes|monthly_page_refresh]
658
+ competitor_changes|icp_visitors|vc_visitors|plan_updates|weekly_release_notes|monthly_page_refresh|
659
+ relink_posts|mention_revisions|fix_autopilot|auto_plan] [--letterbase <id>|--host <site>]
660
660
  [--name <n>] [--trigger competitor_page|competitor_change|visitor_identified|issue_found|build_suggestion|plan_update|custom|
661
661
  schedule]
662
662
  [--prompt <what Letterpress does with each event>] [--slack-channel <C…>] [--instruction <which events>]
663
663
  [--event <name>] [--kind post|landing] [--competitor <host>] [--site <host>]
664
- [--every <n> --unit hours|days|weeks|months] [--facts <a,b>] [--sandbox] [--recipe <id>]
664
+ [--every <n> --unit hours|days|weeks|months] [--facts <a,b>] [--recipe <id>]
665
665
  [--hot-leads-only] [--per-day <n>] [--backfill all|<YYYY-MM-DD>] [--off]
666
666
  Make one (a template fills what you leave out): it starts
667
667
  from now (--backfill also runs it on past events, oldest
668
668
  first), acts on its own, and posts each event it acts on
669
669
  to --slack-channel with its buttons. --trigger schedule
670
670
  --every 2 --unit weeks is a recurring one (on the calendar:
671
- Monday, the 1st); --sandbox always runs its prompt in a Build
672
- sandbox (off, Letterpress decides per event); --facts
671
+ Monday, the 1st); Letterpress decides per run whether
672
+ its prompt needs a Build sandbox; --facts
673
673
  names what each tick carries (merged_changes, page_reads,
674
674
  new_pages_unlinked, striking_queries, losing_pages);
675
- --recipe makes a Plan recipe's own
675
+ --recipe makes a Plan recipe's own; a job Letter runs
676
+ (relink_posts, mention_revisions: --letterbase;
677
+ auto_plan: --host; fix_autopilot) is made, or added
678
+ back where someone removed it, acting as Letter
676
679
  automate set <automation-id> [--name <n>] [--instruction <text>] [--prompt <text>] [--slack-channel <C…>|none]
677
- [--sandbox|--no-sandbox] [--backfill all|<YYYY-MM-DD>] [--on|--off] ...
680
+ [--backfill all|<YYYY-MM-DD>] [--on|--off] ...
678
681
  automate try [<automation-id>] [--trigger <t> --prompt <text> --instruction <text> ...] [--limit <n>]
679
682
  What it would do with the latest events (acts on none)
680
683
  automate runs [--automation <id>] [--status acting|acted|skipped|failed|proposed|rejected] [--limit <n>]
@@ -46,6 +46,8 @@ export async function cmdAsk(ctx) {
46
46
  : ""
47
47
  : "";
48
48
  lines.push(` ${i.status.padEnd(7)} ${i.title}${how}${waits}`, ` ${i.why}`);
49
+ // What the row does and its steps ("Next: …", "Turns on … in Automations"), as Ideas shows them.
50
+ if (i.detail && i.detail !== i.why) lines.push(` ${i.detail}`);
49
51
  }
50
52
  if (r.unmet) lines.push(`Not something we do yet: ${r.unmet}`);
51
53
  if (r.status === "pending") lines.push("(reading the site; ask again in a few seconds)");
@@ -2,9 +2,9 @@
2
2
  // against your filter (--instruction), then done by Letterpress as your prompt (--prompt) says.
3
3
  // options / list / show / add / set / try / runs / approve / reject / send / findings / act / dismiss / remove, through the
4
4
  // same tools the app uses. (A letterbase's writing automations are made and paced with `automations`.) `list` also
5
- // shows the jobs Letter runs for the account (source "letter": internal links, the blog's sync, the site's re-audit,
6
- // Fix autopilot, add-your-name suggestions, Auto-Plan, and each letterbase's writing automations), each with its
7
- // scope; `set <id> --on|--off` turns one on or off.
5
+ // shows the jobs Letter runs for the account (source "letter": a letterbase's internal links, its add-your-name
6
+ // suggestions, Fix autopilot, Auto-Plan, and each letterbase's writing automations), each with its scope: changed with
7
+ // `set`, removed with `remove`, and made or added back with `add --template <job> --letterbase <id>|--host <host>`.
8
8
  // Same flag/positional/io conventions as posts.mjs — no commander, no color.
9
9
 
10
10
  import { CliError } from "../client.mjs";
@@ -31,6 +31,11 @@ const TEMPLATES = [
31
31
  "plan_updates",
32
32
  "weekly_release_notes",
33
33
  "monthly_page_refresh",
34
+ // The jobs Letter runs: one per letterbase (--letterbase), site (--host) or the organization.
35
+ "relink_posts",
36
+ "mention_revisions",
37
+ "fix_autopilot",
38
+ "auto_plan",
34
39
  ];
35
40
  const UNITS = ["hours", "days", "weeks", "months"];
36
41
  const STATUSES = ["pending", "proposed", "acting", "acted", "answered", "skipped", "failed", "rejected"];
@@ -51,10 +56,6 @@ const triggerParams = {
51
56
  facts: { flag: "facts", type: "list" },
52
57
  },
53
58
  };
54
- // --sandbox / --no-sandbox: always run the prompt in a Build sandbox (the repo and Letter's tools); off, Letterpress decides per event.
55
- const sandbox = {
56
- value: (flags) => (flags["no-sandbox"] === true ? false : flags.sandbox === true ? true : undefined),
57
- };
58
59
  // --slack-channel C0123ABCD (repo slack-targets), or none to post nothing.
59
60
  const slackChannel = {
60
61
  value: (flags) =>
@@ -108,6 +109,16 @@ const destination = {
108
109
  },
109
110
  };
110
111
 
112
+ // --letterbase <collection-id> or --host <site>: where a job Letter runs runs (its template's scope).
113
+ const scope = {
114
+ value: (flags) => {
115
+ const letterbase = flagStr(flags.letterbase);
116
+ const host = flagStr(flags.host);
117
+ if (letterbase && host) throw new CliError("Pass --letterbase or --host, not both.");
118
+ return letterbase ? { collection_id: letterbase } : host ? { host } : undefined;
119
+ },
120
+ };
121
+
111
122
  export const ARGS = {
112
123
  describe_automation_options: {},
113
124
  list_event_automations: {},
@@ -117,6 +128,7 @@ export const ARGS = {
117
128
  create_event_automation: {
118
129
  destination,
119
130
  template: { flag: "template", enum: TEMPLATES },
131
+ scope,
120
132
  // --recipe <id>: make the automation a Plan recipe carries (the Plan row's own call).
121
133
  recipe: { flag: "recipe" },
122
134
  name: { flag: "name" },
@@ -125,7 +137,6 @@ export const ARGS = {
125
137
  instruction: { flag: "instruction" },
126
138
  prompt: { flag: "prompt" },
127
139
  slack_channel: slackChannel,
128
- sandbox,
129
140
  enabled,
130
141
  backfill,
131
142
  },
@@ -138,7 +149,6 @@ export const ARGS = {
138
149
  instruction: { flag: "instruction" },
139
150
  prompt: { flag: "prompt" },
140
151
  slack_channel: slackChannel,
141
- sandbox,
142
152
  enabled,
143
153
  backfill,
144
154
  },
@@ -147,6 +147,9 @@ export const ARGS = {
147
147
  refresh_internal_links: {
148
148
  collection_id: { positional: 0, label: "collection-id", required: true },
149
149
  },
150
+ apply_mention_suggestions: {
151
+ collection_id: { positional: 0, label: "collection-id", required: true },
152
+ },
150
153
  };
151
154
 
152
155
  export async function cmdCollection(ctx) {
@@ -190,9 +193,11 @@ export async function cmdCollection(ctx) {
190
193
  return collectionOverview(rest);
191
194
  case "links":
192
195
  return collectionLinks(rest);
196
+ case "mentions":
197
+ return collectionMentions(rest);
193
198
  default:
194
199
  throw new CliError(
195
- `Unknown collection subcommand: ${sub ?? "(none)"}. Try: list, overview, new, config, delete, run, add, queue, headline, run-status, runs, approve, move, cancel, inventory, links`
200
+ `Unknown collection subcommand: ${sub ?? "(none)"}. Try: list, overview, new, config, delete, run, add, queue, headline, run-status, runs, approve, move, cancel, inventory, links, mentions`
196
201
  );
197
202
  }
198
203
  }
@@ -470,3 +475,25 @@ async function collectionLinks(ctx) {
470
475
  );
471
476
  return 0;
472
477
  }
478
+
479
+ // `collection mentions <id> --apply` applies the add-the-mention suggestions whose review window has passed: what
480
+ // the collection's "Add your name where AI cites your posts" automation does on each tick.
481
+ async function collectionMentions(ctx) {
482
+ const { client, flags, positionals, io } = ctx;
483
+ refuseUnknownFlags(ctx, {}, ["apply"]);
484
+ if (flags.apply !== true)
485
+ throw new CliError("Pass --apply to apply the suggestions past their review window (see: suggestions list).");
486
+ const r = await client.callTool(
487
+ "apply_mention_suggestions",
488
+ argsFromFlags(ARGS.apply_mention_suggestions, flags, positionals)
489
+ );
490
+ printResult(io, flags, r, (x) =>
491
+ x.cards.length
492
+ ? [
493
+ `Applied ${x.applied}, failed ${x.failed}, skipped ${x.skipped}.`,
494
+ ...x.cards.map((c) => ` ${c.title} (${c.posts} posts): ${c.outcome}`),
495
+ ].join("\n")
496
+ : "Nothing due: no add-the-mention suggestion is past its review window."
497
+ );
498
+ return 0;
499
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.176.81",
3
+ "version": "0.176.83",
4
4
  "description": "Spin up and manage Letter phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",