@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 +5 -1
- package/lib/commands/build.mjs +7 -1
- package/lib/commands/site.mjs +31 -1
- package/package.json +1 -1
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.
|
|
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
|
package/lib/commands/build.mjs
CHANGED
|
@@ -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:
|
|
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",
|
package/lib/commands/site.mjs
CHANGED
|
@@ -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;
|