@letterstory/cli 0.176.52 → 0.176.54

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.52";
88
+ export const VERSION = "0.176.54";
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;
@@ -117,10 +117,12 @@ export const BOOLEAN_FLAGS = new Set([
117
117
  "keep-key",
118
118
  "delete-key",
119
119
  "keep",
120
- // automate add/set: on or off; only identified visitors marked hot leads.
120
+ // automate add/set: on or off; only identified visitors marked hot leads; run the prompt in a Build sandbox.
121
121
  "on",
122
122
  "off",
123
123
  "hot-leads-only",
124
+ "sandbox",
125
+ "no-sandbox",
124
126
  // admin crm prospect-org: start (or retry) the org prepared for the booked call.
125
127
  "start",
126
128
  // site research-builds: run now even when the stored pass is under an hour old.
@@ -136,6 +138,8 @@ export const BOOLEAN_FLAGS = new Set([
136
138
  // build: repeat an open build's ask; post its progress in the org's Slack channel.
137
139
  "again",
138
140
  "plan-only",
141
+ // build: every change reads Letter's tools from its sandbox.
142
+ "primitives-access",
139
143
  // build list: only Build's Archived tab (--open, already listed, is its Open tab).
140
144
  "archived",
141
145
  // plan pending --queued: only the rows a person queued for later.
@@ -198,6 +202,8 @@ export const BOOLEAN_FLAGS = new Set([
198
202
  "no-control",
199
203
  "own-beat",
200
204
  "refresh",
205
+ // ask --more: every row that fits, not only the top ones.
206
+ "more",
201
207
  "preview",
202
208
  "with-preview",
203
209
  // repo …: the repo is a GitLab project (--repo group/subgroup/project).
@@ -633,17 +639,22 @@ Reviewers:
633
639
  automate list Your automations: an event, which ones it acts on,
634
640
  and what Letterpress then does
635
641
  automate show <automation-id>
636
- automate add [--template post_new_issues|recommended_actions|competitor_pages|icp_visitors|vc_visitors|plan_updates]
637
- [--name <n>] [--trigger competitor_page|visitor_identified|issue_found|build_suggestion|plan_update|custom]
642
+ automate add [--template post_new_issues|fix_new_issues|recommended_actions|competitor_pages|competitor_takes|
643
+ icp_visitors|vc_visitors|plan_updates|weekly_release_notes|monthly_page_refresh]
644
+ [--name <n>] [--trigger competitor_page|visitor_identified|issue_found|build_suggestion|plan_update|custom|schedule]
638
645
  [--prompt <what Letterpress does with each event>] [--slack-channel <C…>] [--instruction <which events>]
639
646
  [--event <name>] [--kind post|landing] [--competitor <host>] [--site <host>]
647
+ [--every <n> --unit hours|days|weeks|months] [--sandbox]
640
648
  [--hot-leads-only] [--per-day <n>] [--backfill all|<YYYY-MM-DD>] [--off]
641
649
  Make one (a template fills what you leave out): it starts
642
650
  from now (--backfill also runs it on past events, oldest
643
651
  first), acts on its own, and posts each event it acts on
644
- to --slack-channel with its buttons
652
+ to --slack-channel with its buttons. --trigger schedule
653
+ --every 2 --unit weeks is a recurring one (on the calendar:
654
+ Monday, the 1st); --sandbox always runs its prompt in a Build
655
+ sandbox (off, Letterpress decides per event)
645
656
  automate set <automation-id> [--name <n>] [--instruction <text>] [--prompt <text>] [--slack-channel <C…>|none]
646
- [--backfill all|<YYYY-MM-DD>] [--on|--off] ...
657
+ [--sandbox|--no-sandbox] [--backfill all|<YYYY-MM-DD>] [--on|--off] ...
647
658
  automate try [<automation-id>] [--trigger <t> --prompt <text> --instruction <text> ...] [--limit <n>]
648
659
  What it would do with the latest events (acts on none)
649
660
  automate runs [--automation <id>] [--status acting|acted|skipped|failed|proposed|rejected] [--limit <n>]
@@ -1678,17 +1689,17 @@ Mail:
1678
1689
  (any phone format; country as a name or a 2-letter code)
1679
1690
 
1680
1691
  Insights:
1681
- ask [<words…>] [--outcome geo|seo|…] [--lens fix|build] [--site host]
1692
+ ask [<words…>] [--outcome geo|seo|…] [--more] [--lens fix|build] [--site host]
1682
1693
  Ask Letterpress what to do next: the plan for an outcome (the
1683
1694
  Plan tab's box), what's broken (--lens fix, most pressing
1684
1695
  first), what to build (--lens build), or your own words. Nothing asked:
1685
1696
  each list's head. The same answer as the console and Slack.
1686
1697
  plan auto [activate|disable] [--site host] [--veto-hours <1-168>|off] [--pace-hours <1-168>]
1687
1698
  [--delivery build|build_pr|build_merge]
1688
- [--aggression low|medium|high|xhigh] [--goal geo|seo|agent_conviction|inbound_conversion|any]
1699
+ [--aggression low|medium|high|xhigh] [--strategies geo,seo,agent_conviction,inbound_conversion|any]
1689
1700
  [--slack-channel <C…>|none]
1690
1701
  Auto-Plan on a site: Letterpress picks its strategies and builds
1691
- them (--goal: one goal's strategies, else any); each pick waits --veto-hours for a veto. Settings shown when none given.
1702
+ them under the site's strategies (--strategies, any by default; the Plan reads the same set); each pick waits --veto-hours for a veto. Settings shown when none given.
1692
1703
  plan pending [--site host] What Auto-Plan picked and is waiting to build
1693
1704
  plan veto <proposal-id> Veto one: it is never built
1694
1705
  plan start <proposal-id> Start one now (a Needs you pick, or one waiting out its review window)
@@ -1726,6 +1737,7 @@ Build (a change to your site, or launching what isn't code; the Build box's own
1726
1737
  build <words…> [--prompt <text>] [--site <host>] [--subject <host/path>]... [--action build|build_pr|build_merge|slack|linear]
1727
1738
  [--attach <path>]... [--attach-url <name>=<https url>]... [--attach-ref <path>]... [--attach-url-ref <name>=<https url>]...
1728
1739
  [--harness letterpress-1|claude-code|codex] [--effort low|medium|high|xhigh] [--notify-slack] [--again] [--new-site]
1740
+ [--primitives-access]
1729
1741
  Build from your words: "add a pricing page" opens a preview or PR;
1730
1742
  "launch a phantom for us", "set up mailers" or "onboard my blog" is
1731
1743
  done by Build itself (a phantom site buys its own domain), and the
@@ -1,7 +1,7 @@
1
1
  // `ask` — ask Letterpress, the one door (docs/architecture/one-recipe-engine.md §9b): what should
2
2
  // this account do next. The same answer as the console's Plan, Build and Fix tabs and Slack.
3
3
  // ask → each list's head: what's broken, what to build
4
- // ask --outcome geo|seo|… → the plan for that outcome (the Plan tab's box)
4
+ // ask --outcome geo|seo|… → the plan for that outcome (Ideas filtered to it); --more: every row that fits
5
5
  // ask --strategy <id> → a typed strategy's next actions (plan strategies)
6
6
  // ask --lens fix | --lens build → what's broken (most pressing first) | what to build
7
7
  // ask "<words…>" → in your own words; what nothing does comes back as unmet
@@ -21,13 +21,15 @@ export const ARGS = {
21
21
  found: "found",
22
22
  // --refresh: decide every strategy again from the facts as they are now (the standing fit is read otherwise).
23
23
  refresh: { flag: "refresh", type: "bool" },
24
+ // --more: every row that fits, not only the top ones (the Ideas list's Show all).
25
+ more: { flag: "more", type: "bool" },
24
26
  domain: "site",
25
27
  },
26
28
  };
27
29
 
28
30
  export async function cmdAsk(ctx) {
29
31
  const { client, flags, io } = ctx;
30
- refuseUnknownFlags(ctx, {}, ["outcome", "strategy", "lens", "words", "found", "site", "refresh"]);
32
+ refuseUnknownFlags(ctx, {}, ["outcome", "strategy", "lens", "words", "found", "site", "refresh", "more"]);
31
33
  const args = argsFromFlags(ARGS.ask_letterpress, flags, ctx.positionals, {});
32
34
  if ([args.outcome, args.strategy_id, args.lens, args.words].filter(Boolean).length > 1)
33
35
  throw new CliError("One ask at a time: --outcome, --strategy <id>, --lens fix|build, or your words.");
@@ -7,15 +7,28 @@
7
7
  import { CliError } from "../client.mjs";
8
8
  import { printResult, ok, argsFromFlags, flagList, flagStr, refuseUnknownFlags } from "./shared.mjs";
9
9
 
10
- const TRIGGERS = ["competitor_page", "visitor_identified", "issue_found", "build_suggestion", "plan_update", "custom"];
10
+ const TRIGGERS = [
11
+ "competitor_page",
12
+ "visitor_identified",
13
+ "issue_found",
14
+ "build_suggestion",
15
+ "plan_update",
16
+ "custom",
17
+ "schedule",
18
+ ];
11
19
  const TEMPLATES = [
12
20
  "post_new_issues",
21
+ "fix_new_issues",
13
22
  "recommended_actions",
14
23
  "competitor_pages",
24
+ "competitor_takes",
15
25
  "icp_visitors",
16
26
  "vc_visitors",
17
27
  "plan_updates",
28
+ "weekly_release_notes",
29
+ "monthly_page_refresh",
18
30
  ];
31
+ const UNITS = ["hours", "days", "weeks", "months"];
19
32
  const STATUSES = ["pending", "proposed", "acting", "acted", "skipped", "failed", "rejected"];
20
33
 
21
34
  const triggerParams = {
@@ -26,8 +39,15 @@ const triggerParams = {
26
39
  hot_leads_only: { flag: "hot-leads-only", type: "bool" },
27
40
  event: { flag: "event" },
28
41
  per_day: { flag: "per-day", type: "num" },
42
+ // --trigger schedule --every 2 --unit weeks: a recurring automation, on the calendar (Monday, the 1st).
43
+ every: { flag: "every", type: "num" },
44
+ unit: { flag: "unit", enum: UNITS },
29
45
  },
30
46
  };
47
+ // --sandbox / --no-sandbox: always run the prompt in a Build sandbox (the repo and Letter's tools); off, Letterpress decides per event.
48
+ const sandbox = {
49
+ value: (flags) => (flags["no-sandbox"] === true ? false : flags.sandbox === true ? true : undefined),
50
+ };
31
51
  // --slack-channel C0123ABCD (repo slack-targets), or none to post nothing.
32
52
  const slackChannel = {
33
53
  value: (flags) =>
@@ -96,6 +116,7 @@ export const ARGS = {
96
116
  instruction: { flag: "instruction" },
97
117
  prompt: { flag: "prompt" },
98
118
  slack_channel: slackChannel,
119
+ sandbox,
99
120
  enabled,
100
121
  backfill,
101
122
  },
@@ -108,6 +129,7 @@ export const ARGS = {
108
129
  instruction: { flag: "instruction" },
109
130
  prompt: { flag: "prompt" },
110
131
  slack_channel: slackChannel,
132
+ sandbox,
111
133
  enabled,
112
134
  backfill,
113
135
  },
@@ -50,6 +50,8 @@ const ARGS = {
50
50
  files: { value: (_flags, _positionals, extra) => (extra.files?.length ? extra.files : undefined) },
51
51
  delivery: { flag: "action", enum: ["build", "build_pr", "build_merge", "slack", "linear"] },
52
52
  notify_slack: { flag: "notify-slack", type: "bool" },
53
+ // --primitives-access: every change reads Letter's tools from its sandbox (a read-only MCP key beside the repo).
54
+ primitives_access: { flag: "primitives-access", type: "bool" },
53
55
  harness: { flag: "harness", enum: ["letterpress-1", "claude-code", "codex"] },
54
56
  // --branch <name>: where its changes start and its pull requests go; omitted, the branch that builds the site.
55
57
  base_branch: "branch",
@@ -2,7 +2,7 @@
2
2
  // organization's own domain). The same calls as the Plan's modal and its sidebar's Pending rows.
3
3
  // plan auto → get_auto_plan
4
4
  // plan auto [activate|disable] [--veto-hours <1-168>|off] [--pace-hours <1-168>] [--delivery build|build_pr|build_merge]
5
- // [--aggression low|medium|high|xhigh] [--goal geo|seo|agent_conviction|inbound_conversion|any]
5
+ // [--aggression low|medium|high|xhigh] [--strategies geo,seo|any]
6
6
  // [--slack-channel <C…>|none] → set_auto_plan
7
7
  // plan pending → list_auto_plan_proposals (status pending)
8
8
  // plan veto <proposal-id> → veto_auto_plan_proposal
@@ -67,15 +67,23 @@ export const ARGS = {
67
67
  // How far each pick goes: a preview, a pull request, or one merged once ready.
68
68
  delivery: { flag: "delivery", enum: ["build", "build_pr", "build_merge"] },
69
69
  aggression: { flag: "aggression", enum: ["low", "medium", "high", "xhigh"] },
70
- // --goal geo picks strategies for that goal only; --goal any (the default) reads every strategy.
71
- goal: {
70
+ // --strategies geo,seo: the site's strategies (what the Plan reads under, what Auto-Plan picks from), replaced
71
+ // whole; --strategies any (or none) reads every strategy.
72
+ strategies: {
72
73
  value: (flags) => {
73
- const v = flagStr(flags.goal);
74
+ const v = flagStr(flags.strategies);
74
75
  if (v === undefined) return undefined;
75
- if (v === "any") return null;
76
- if (!["agent_conviction", "geo", "seo", "inbound_conversion"].includes(v))
77
- throw new CliError("--goal takes agent_conviction, geo, seo, inbound_conversion, or any.");
78
- return v;
76
+ if (v === "any" || v === "") return [];
77
+ const list = v
78
+ .split(",")
79
+ .map((x) => x.trim())
80
+ .filter(Boolean);
81
+ for (const g of list)
82
+ if (!["agent_conviction", "geo", "seo", "inbound_conversion"].includes(g))
83
+ throw new CliError(
84
+ "--strategies takes agent_conviction, geo, seo, inbound_conversion (comma-separated), or any."
85
+ );
86
+ return list;
79
87
  },
80
88
  },
81
89
  slack_channel: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.176.52",
3
+ "version": "0.176.54",
4
4
  "description": "Spin up and manage Letter phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",