@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 +13 -10
- package/lib/commands/ask.mjs +2 -0
- package/lib/commands/automate.mjs +19 -9
- package/lib/commands/collection.mjs +28 -1
- package/package.json +1 -1
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.
|
|
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
|
|
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>] [--
|
|
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);
|
|
672
|
-
|
|
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
|
-
[--
|
|
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>]
|
package/lib/commands/ask.mjs
CHANGED
|
@@ -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":
|
|
6
|
-
// Fix autopilot,
|
|
7
|
-
//
|
|
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
|
+
}
|