@letterstory/cli 0.177.4 → 0.177.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.
package/lib/cli.mjs CHANGED
@@ -87,7 +87,7 @@ import {
87
87
  import { fetchParkedSections, withoutParked } from "./parked.mjs";
88
88
 
89
89
  // Matches cli/package.json; publish-cli.yml rewrites both to the published version.
90
- export const VERSION = "0.177.4";
90
+ export const VERSION = "0.177.6";
91
91
 
92
92
  /** The exit code for a gate refusal (CARD_REQUIRED, PAYMENT_FAILED, OUT_OF_TOKENS): 1 is a plain error. */
93
93
  export const EXIT_PAYMENT_REQUIRED = 3;
@@ -152,6 +152,8 @@ export const BOOLEAN_FLAGS = new Set([
152
152
  // build: repeat an open build's ask; post its progress in the org's Slack channel.
153
153
  "again",
154
154
  "plan-only",
155
+ // build: the words are a request to build, never read as a question.
156
+ "build-it",
155
157
  // build: every change reads Letter's tools from its sandbox.
156
158
  "primitives-access",
157
159
  // build list: only Build's Archived tab (--open, already listed, is its Open tab).
@@ -1879,6 +1881,8 @@ The Site (one model of your whole marketing site, keyed by host and path):
1879
1881
  site highlight <domain> [--off] Highlight a competitor; its changes lead the top changes
1880
1882
  site prospect <url> [--pages <n>] [--max-pages <n>] [--depth <n>]
1881
1883
  Staff: any company's site, no account: what Fix would list, each PR
1884
+ site suggest <host> --idea <id> | --draft-id <id>... | --pattern <folder> | --item <id> --path <path>
1885
+ Open PR on a suggestion the Site stores, as one job
1882
1886
 
1883
1887
  Repo changes (Claude Code edits your repo in a sandbox; the Letter GitHub App opens a PR):
1884
1888
  repo repos [--gitlab [--search <text>] [--page n]] Repos the Letter GitHub App can reach (--gitlab: the GitLab
@@ -42,6 +42,10 @@ const ARGS = {
42
42
  },
43
43
  // The words after `build`, or --prompt for a longer ask.
44
44
  prompt: { value: (flags, positionals) => flagStr(flags.prompt) ?? (positionals.join(" ").trim() || undefined) },
45
+ // --ask "<words>": the person's own words, when --prompt was written from them.
46
+ ask: "ask",
47
+ // --build-it: the words are a request to build, never read as a question.
48
+ asked_to_build: { flag: "build-it", type: "bool" },
45
49
  domain: "site",
46
50
  // --subject host/path (repeatable): the pages it is about.
47
51
  subjects: { flag: "subject", type: "list" },
@@ -50,7 +54,7 @@ const ARGS = {
50
54
  files: { value: (_flags, _positionals, extra) => (extra.files?.length ? extra.files : undefined) },
51
55
  delivery: { flag: "action", enum: ["build", "build_pr", "build_merge", "slack", "linear"] },
52
56
  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).
57
+ // --primitives-access: its change reads Letter's tools from its sandbox (a read-only MCP key beside the repo).
54
58
  primitives_access: { flag: "primitives-access", type: "bool" },
55
59
  harness: { flag: "harness", enum: ["letterpress-1", "claude-code", "codex"] },
56
60
  // --branch <name>: where its changes start and its pull requests go; omitted, the branch that builds the site.
@@ -246,6 +250,8 @@ async function buildStart(ctx) {
246
250
  const { client, flags, io } = ctx;
247
251
  refuseUnknownFlags(ctx, {}, [
248
252
  "prompt",
253
+ "ask",
254
+ "build-it",
249
255
  "site",
250
256
  "subject",
251
257
  "recipe",
@@ -15,12 +15,14 @@
15
15
  // site competitors → list_competitor_sites site watch-competitors [d] → watch_competitor_sites
16
16
  // site highlight <domain> [--off] → set_competitor_highlight
17
17
  // site prospect <url> → read_prospect_site (staff: any company's site, no account, nothing stored)
18
+ // site suggest <host> --idea <id> | --draft-id <id>… | --pattern <folder> | --item <id> --path <p> → start_suggestion
18
19
 
19
20
  import { writeFileSync } from "node:fs";
20
21
 
21
22
  import { CliError } from "../client.mjs";
22
23
  import {
23
24
  argsFromFlags,
25
+ flagCsv,
24
26
  flagList,
25
27
  flagNum,
26
28
  flagStr,
@@ -193,6 +195,14 @@ const ARGS = {
193
195
  decision: { flag: "decision", required: true, enum: ["accept", "dismiss"] },
194
196
  reason: "reason",
195
197
  },
198
+ start_suggestion: {
199
+ domain: { positional: 0, required: true, label: "host" },
200
+ idea_id: "idea",
201
+ draft_ids: { value: (flags) => (flagCsv(flags["draft-id"]).length ? flagCsv(flags["draft-id"]) : undefined) },
202
+ pattern_folder: "pattern",
203
+ observation_id: "item",
204
+ path: "path",
205
+ },
196
206
  fix_site_observations: {
197
207
  host: { positional: 0, required: true, label: "host" },
198
208
  // Every positional after the host, plus --observation <id> (repeatable).
@@ -264,9 +274,11 @@ export async function cmdSite(ctx) {
264
274
  return siteHighlight(rest);
265
275
  case "prospect":
266
276
  return siteProspect(rest);
277
+ case "suggest":
278
+ return siteSuggest(rest);
267
279
  default:
268
280
  throw new CliError(
269
- `Unknown site subcommand: ${sub ?? "(none)"}. Try: list, map, refresh, unmap, tree, builds, metrics, page, observations, observe, dismiss, families, decide-family, proposals, propose, decide, fix, competitors, watch-competitors, highlight, prospect`
281
+ `Unknown site subcommand: ${sub ?? "(none)"}. Try: list, map, refresh, unmap, tree, builds, metrics, page, observations, observe, dismiss, families, decide-family, proposals, propose, decide, fix, competitors, watch-competitors, highlight, prospect, suggest`
270
282
  );
271
283
  }
272
284
  }
@@ -396,6 +408,24 @@ async function siteMap(ctx) {
396
408
  return 0;
397
409
  }
398
410
 
411
+ /** A suggestion the Site stores, started as the Site's Open PR does: one job, its words read from what Letter stores. */
412
+ async function siteSuggest(ctx) {
413
+ const { client, flags, io } = ctx;
414
+ refuseUnknownFlags(ctx, ARGS.start_suggestion, ["idea", "draft-id", "pattern", "item", "path"]);
415
+ const args = argsFromFlags(ARGS.start_suggestion, flags, ctx.positionals);
416
+ const named = [args.idea_id, args.draft_ids, args.pattern_folder, args.observation_id].filter(Boolean).length;
417
+ if (named !== 1 || (args.observation_id && !args.path))
418
+ throw new CliError(
419
+ "Name one suggestion: site suggest <host> --idea <id> | --draft-id <id>… | --pattern <folder> | --item <id> --path <path>"
420
+ );
421
+ const result = await client.callTool("start_suggestion", args);
422
+ printResult(io, flags, result, (r) => {
423
+ if (r.status !== "started") return r.message ?? r.status;
424
+ return `Started: ${r.build.title} (${r.build.id})`;
425
+ });
426
+ return 0;
427
+ }
428
+
399
429
  /** Any company's site read as the Site and Fix read a customer's: what Fix would list, and each pull request. */
400
430
  async function siteProspect(ctx) {
401
431
  const { client, flags, io } = ctx;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.177.4",
3
+ "version": "0.177.6",
4
4
  "description": "Spin up and manage Letter phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",