@letterstory/cli 0.176.83 → 0.176.85
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 +7 -2
- package/lib/commands/automate.mjs +1 -0
- package/lib/commands/insights.mjs +16 -2
- package/lib/commands/replicate.mjs +13 -2
- package/lib/commands/site.mjs +33 -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.85";
|
|
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;
|
|
@@ -656,7 +656,7 @@ Reviewers:
|
|
|
656
656
|
automate show <automation-id>
|
|
657
657
|
automate add [--template post_new_issues|recommended_actions|competitor_pages|competitor_takes|
|
|
658
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>]
|
|
659
|
+
relink_posts|mention_revisions|fix_autopilot|auto_plan|ai_portrayal] [--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>]
|
|
@@ -1764,6 +1764,9 @@ Insights:
|
|
|
1764
1764
|
insights page <page> [--period 14d|30d|90d] [--question <prompt-id>] [--filter all|won] [--limit n]
|
|
1765
1765
|
One cited page: the AI answers that linked it, word for word
|
|
1766
1766
|
insights mentions [--site phantoms|<key>]... Measure → Pages → Naming you: which posts on your phantom sites name you
|
|
1767
|
+
insights portrayal [--period 14d|30d|90d] How AI answers describe you, what they get wrong
|
|
1768
|
+
insights claims [--period …] [--verdict true|outdated|wrong|unknown|unchecked] [--entity self|<rival>] [--limit n]
|
|
1769
|
+
The claims AI answers make about you, each checked against your facts
|
|
1767
1770
|
insights competitors [--period 14d|30d|90d] GEO → Competitors: share of mentions per brand, is it moving, by engine/question
|
|
1768
1771
|
insights question <key> [--period 14d|30d|90d] [--filter all|named_you|rival_not_you|no_one] [--limit n]
|
|
1769
1772
|
One By-question row: the wordings asked and the AI answers
|
|
@@ -1838,6 +1841,8 @@ The Site (one model of your whole marketing site, keyed by host and path):
|
|
|
1838
1841
|
site competitors [--since <iso> | --days <n>] [--limit <n>] Tracked competitors' sites: new and removed pages, by folder
|
|
1839
1842
|
site watch-competitors [domain] [--limit <n>] Map competitors' sitemaps (the next few unmapped, or one now)
|
|
1840
1843
|
site highlight <domain> [--off] Highlight a competitor; its changes lead the top changes
|
|
1844
|
+
site prospect <url> [--pages <n>] [--max-pages <n>] [--depth <n>]
|
|
1845
|
+
Staff: any company's site, no account: what Fix would list, each PR
|
|
1841
1846
|
|
|
1842
1847
|
Repo changes (Claude Code edits your repo in a sandbox; the Letter GitHub App opens a PR):
|
|
1843
1848
|
repo repos [--gitlab [--search <text>] [--page n]] Repos the Letter GitHub App can reach (--gitlab: the GitLab
|
|
@@ -36,6 +36,7 @@ const TEMPLATES = [
|
|
|
36
36
|
"mention_revisions",
|
|
37
37
|
"fix_autopilot",
|
|
38
38
|
"auto_plan",
|
|
39
|
+
"ai_portrayal",
|
|
39
40
|
];
|
|
40
41
|
const UNITS = ["hours", "days", "weeks", "months"];
|
|
41
42
|
const STATUSES = ["pending", "proposed", "acting", "acted", "answered", "skipped", "failed", "rejected"];
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// network, single-post, and top-posts levels (ported from phantomstory-cli's
|
|
3
3
|
// insights.ts); plus Measure's Overview (`summary`), GEO → Competitors
|
|
4
4
|
// (`competitors`), AI Visibility Bank (`bank`) and phantom Page Access (`access`);
|
|
5
|
-
// Measure → Reads (`reads`), Questions (`questions`), Pages (`pages`, one page's answers `page`)
|
|
5
|
+
// Measure → Reads (`reads`), Questions (`questions`), Pages (`pages`, one page's answers `page`), the posts naming you (`mentions`)
|
|
6
|
+
// and Portrayal (`portrayal`, its claims `claims`: how AI answers describe you), and the live feed of what
|
|
6
7
|
// is reading the phantom sites (`activity`) — the same reads the web app renders. Search Console's
|
|
7
8
|
// property: which ones the connected Google account can read (`properties`) and the one tracked (`property`).
|
|
8
9
|
|
|
@@ -53,6 +54,15 @@ export const ARGS = {
|
|
|
53
54
|
list_site_mentions: {
|
|
54
55
|
sites: { flag: "site", type: "list" },
|
|
55
56
|
},
|
|
57
|
+
get_ai_portrayal: {
|
|
58
|
+
period: { flag: "period", enum: PERIODS },
|
|
59
|
+
},
|
|
60
|
+
list_answer_claims: {
|
|
61
|
+
period: { flag: "period", enum: PERIODS },
|
|
62
|
+
verdict: { flag: "verdict", enum: ["true", "outdated", "wrong", "unknown", "unchecked"] },
|
|
63
|
+
entity: { flag: "entity" },
|
|
64
|
+
limit: { flag: "limit", type: "num" },
|
|
65
|
+
},
|
|
56
66
|
get_recent_activity: {
|
|
57
67
|
phantom_id: { flag: "phantom" },
|
|
58
68
|
limit: { flag: "limit", type: "num" },
|
|
@@ -101,6 +111,10 @@ export async function cmdInsights(ctx) {
|
|
|
101
111
|
return call(rest, "get_cited_page_answers");
|
|
102
112
|
case "mentions":
|
|
103
113
|
return call(rest, "list_site_mentions");
|
|
114
|
+
case "portrayal":
|
|
115
|
+
return call(rest, "get_ai_portrayal");
|
|
116
|
+
case "claims":
|
|
117
|
+
return call(rest, "list_answer_claims");
|
|
104
118
|
case "activity":
|
|
105
119
|
return call(rest, "get_recent_activity");
|
|
106
120
|
case "properties":
|
|
@@ -109,7 +123,7 @@ export async function cmdInsights(ctx) {
|
|
|
109
123
|
return call(rest, "select_gsc_property");
|
|
110
124
|
default:
|
|
111
125
|
throw new CliError(
|
|
112
|
-
`Unknown insights subcommand: ${sub ?? "(none)"}. Try: summary, reads, questions, pages, page, mentions, competitors, question, detection, activity, site, post, top, bank, access, properties, property`
|
|
126
|
+
`Unknown insights subcommand: ${sub ?? "(none)"}. Try: summary, reads, questions, pages, page, mentions, portrayal, claims, competitors, question, detection, activity, site, post, top, bank, access, properties, property`
|
|
113
127
|
);
|
|
114
128
|
}
|
|
115
129
|
}
|
|
@@ -203,8 +203,19 @@ function replicationDetail(ctx, result) {
|
|
|
203
203
|
}
|
|
204
204
|
if (r.dropped?.length)
|
|
205
205
|
lines.push("", `The site's own code left out (needs its builder's runtime): ${r.dropped.length}`);
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
// The runs as get_replication words them (lib/replicator/activity.ts): the latest in full, older ones in a line.
|
|
207
|
+
const runs = r.activity ?? [];
|
|
208
|
+
if (runs.length) {
|
|
209
|
+
lines.push("", "Activity:");
|
|
210
|
+
runs.forEach((run, i) => {
|
|
211
|
+
lines.push(` ${run.headline}`);
|
|
212
|
+
if (i > 0) return;
|
|
213
|
+
for (const e of run.events)
|
|
214
|
+
lines.push(
|
|
215
|
+
` ${e.at} ${e.title}${e.count > 1 ? ` (x${e.count})` : ""}${e.link ? ` ${e.link.href}` : ""}`
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
208
219
|
return lines.join("\n");
|
|
209
220
|
});
|
|
210
221
|
return 0;
|
package/lib/commands/site.mjs
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
// site decide <proposal> → decide_site_proposal site fix <host> <id>… → fix_site_observations
|
|
15
15
|
// site competitors → list_competitor_sites site watch-competitors [d] → watch_competitor_sites
|
|
16
16
|
// site highlight <domain> [--off] → set_competitor_highlight
|
|
17
|
+
// site prospect <url> → read_prospect_site (staff: any company's site, no account, nothing stored)
|
|
17
18
|
|
|
18
19
|
import { writeFileSync } from "node:fs";
|
|
19
20
|
|
|
@@ -69,6 +70,12 @@ const ARGS = {
|
|
|
69
70
|
skip_suggest: { flag: "skip-suggest", type: "bool" },
|
|
70
71
|
},
|
|
71
72
|
unmap_site: { host: { positional: 0, required: true, label: "host" } },
|
|
73
|
+
read_prospect_site: {
|
|
74
|
+
url: { positional: 0, required: true, label: "url" },
|
|
75
|
+
pages: { flag: "pages", type: "num" },
|
|
76
|
+
max_pages: { flag: "max-pages", type: "num" },
|
|
77
|
+
depth: { flag: "depth", type: "num" },
|
|
78
|
+
},
|
|
72
79
|
get_llms_txt: { host: { positional: 0, required: true, label: "host" } },
|
|
73
80
|
get_site_tree: {
|
|
74
81
|
host: { positional: 0, required: true, label: "host" },
|
|
@@ -255,9 +262,11 @@ export async function cmdSite(ctx) {
|
|
|
255
262
|
return siteWatchCompetitors(rest);
|
|
256
263
|
case "highlight":
|
|
257
264
|
return siteHighlight(rest);
|
|
265
|
+
case "prospect":
|
|
266
|
+
return siteProspect(rest);
|
|
258
267
|
default:
|
|
259
268
|
throw new CliError(
|
|
260
|
-
`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`
|
|
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`
|
|
261
270
|
);
|
|
262
271
|
}
|
|
263
272
|
}
|
|
@@ -387,6 +396,29 @@ async function siteMap(ctx) {
|
|
|
387
396
|
return 0;
|
|
388
397
|
}
|
|
389
398
|
|
|
399
|
+
/** Any company's site read as the Site and Fix read a customer's: what Fix would list, and each pull request. */
|
|
400
|
+
async function siteProspect(ctx) {
|
|
401
|
+
const { client, flags, io } = ctx;
|
|
402
|
+
refuseUnknownFlags(ctx, ARGS.read_prospect_site);
|
|
403
|
+
const read = await client.callTool(
|
|
404
|
+
"read_prospect_site",
|
|
405
|
+
argsFromFlags(ARGS.read_prospect_site, flags, ctx.positionals)
|
|
406
|
+
);
|
|
407
|
+
printResult(io, flags, read, (r) => {
|
|
408
|
+
const fixFor = new Map(r.fixes.map((f) => [f.itemId, f]));
|
|
409
|
+
const head = [
|
|
410
|
+
`${r.host}${r.platform ? ` (built with ${r.platform.name})` : ""}: ${r.map.pages} pages mapped${r.map.truncated ? ` of ${r.map.listed} listed` : ""}, ${r.read.answered} of ${r.read.pages} read, ${r.open.total} open issues`,
|
|
411
|
+
...(r.note ? [r.note] : []),
|
|
412
|
+
];
|
|
413
|
+
const rows = r.issues.map((i) => {
|
|
414
|
+
const fix = fixFor.get(i.id);
|
|
415
|
+
return ` ${GLYPH[i.tone] ?? "·"} ${i.summary} ${i.path}${fix ? `\n PR: ${fix.title} (${fix.branch})` : ""}`;
|
|
416
|
+
});
|
|
417
|
+
return [...head, ...rows].join("\n");
|
|
418
|
+
});
|
|
419
|
+
return 0;
|
|
420
|
+
}
|
|
421
|
+
|
|
390
422
|
async function siteRefresh(ctx) {
|
|
391
423
|
const { client, flags, io } = ctx;
|
|
392
424
|
refuseUnknownFlags(ctx, ARGS.refresh_site);
|