@letterstory/cli 0.108.1 → 0.108.4
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 +4 -3
- package/lib/commands/admin.mjs +19 -1
- package/lib/commands/onboard.mjs +2 -0
- package/lib/commands/site.mjs +8 -0
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -60,7 +60,7 @@ import {
|
|
|
60
60
|
} from "./commands.mjs";
|
|
61
61
|
|
|
62
62
|
// Keep in sync with cli/package.json.
|
|
63
|
-
export const VERSION = "0.108.
|
|
63
|
+
export const VERSION = "0.108.4";
|
|
64
64
|
|
|
65
65
|
// Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
|
|
66
66
|
// can't accidentally swallow the id as --json's value. Every `type: "bool"` flag (and its
|
|
@@ -623,7 +623,8 @@ Propose lane (the Fleet wizard's Plan and Name screens; persists nothing, spends
|
|
|
623
623
|
What does this org still need? (the router — reads only, decides nothing):
|
|
624
624
|
${bin === "phantom" ? "onboard" : "phantom-onboard"} primitives [<domain>] [--retry] The primitives that fit a domain (default:
|
|
625
625
|
yours), each with the exact call that sets
|
|
626
|
-
it up — the plan Instances shows
|
|
626
|
+
it up — the plan Instances shows;
|
|
627
|
+
--primitives kernel,phantom keeps only those
|
|
627
628
|
${bin === "phantom" ? "onboard" : "phantom-onboard"} plan Every onboarding step (context, main blog,
|
|
628
629
|
Planner, CMS, phantom) with its status:
|
|
629
630
|
done / running / ready / blocked, and what
|
|
@@ -1160,7 +1161,7 @@ The Site (one model of your whole marketing site, keyed by host and path):
|
|
|
1160
1161
|
site refresh <host> [--force-map] [--force-audit] [--skip-observe]
|
|
1161
1162
|
Run the daily loop now: re-map, re-audit, signals, Jev
|
|
1162
1163
|
site unmap <host> Remove a host and everything hanging off it
|
|
1163
|
-
site tree <host> [path] [--depth <n>] [--unfold] [--since <iso>]
|
|
1164
|
+
site tree <host> [path] [--depth <n>] [--unfold] [--since <iso>] [--focus-size <n|all>]
|
|
1164
1165
|
The tree one folder at a time; --since adds +N/-N per folder
|
|
1165
1166
|
site page <host> <path> One page: facts, bindings, raw URLs, open items
|
|
1166
1167
|
site observations <host> [path] [--state open|dismissed|resolved] [--tone risk|warn|info] [--key <k>] [--since <iso>] [--min-reach <0..1>]
|
package/lib/commands/admin.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// The old names — evals, eval-add, eval-set, proposals, propose-eval, capture-eval, reject-eval — still work.)
|
|
15
15
|
// admin evals scoreboard|cases|runs|run|triage|guard|retire … (fleet:read / tuning:experiment)
|
|
16
16
|
// admin fleet-experiments list|articles|clients|mark|unmark|new|assign|assign-eligible|assign-orgs|spin-up|apply|start|stop|decide … (fleet:read / fleet:experiment)
|
|
17
|
-
// admin crm import|capture|outreach|signals|record|signal|accounts|account|score|slack-channels|link-slack|white-glove|set-white-glove|churn|record-churn|undo-churn|onboard|onboarding|retry-onboarding|onboard-plan|payment-link … (crm:read / crm:write)
|
|
17
|
+
// admin crm import|capture|outreach|signals|record|signal|accounts|account|score|slack-channels|link-slack|white-glove|set-white-glove|churn|revenue|record-churn|undo-churn|onboard|onboarding|retry-onboarding|onboard-plan|payment-link … (crm:read / crm:write)
|
|
18
18
|
// admin spend summary|reconcile|pnl|entries|add|import|usage|remove|reference|stripe-sync|services|service … (spend:read / spend:write)
|
|
19
19
|
// admin brand-detection <org-id> (fleet:read)
|
|
20
20
|
// admin brand-detection <org-id> [--term t]... [--exact t]... [--exclude p]... [--note "…"] | --clear [--note "…"] (fleet:instrument)
|
|
@@ -3325,6 +3325,24 @@ async function cmdAdminCrm(ctx) {
|
|
|
3325
3325
|
].join("\n")
|
|
3326
3326
|
);
|
|
3327
3327
|
}
|
|
3328
|
+
case "revenue": {
|
|
3329
|
+
// admin crm revenue — monthly NRR/GRR since 2026-08-10, the bridge, spend per client (docs/architecture/retention.md).
|
|
3330
|
+
const pct = (x) => (x == null ? "—" : `${(x * 100).toFixed(1)}%`);
|
|
3331
|
+
const usd = (cents) => `$${Math.round(cents / 100).toLocaleString("en-US")}`;
|
|
3332
|
+
return out(await staffRequest(ctx, "GET", `${CRM}/revenue`), (r) =>
|
|
3333
|
+
[
|
|
3334
|
+
r.headline
|
|
3335
|
+
? `${r.headline.month}: NRR ${pct(r.headline.nrr)} · GRR ${pct(r.headline.grr)} · expansion ${usd(r.headline.expansionCents)} · avg ${r.headline.avgCents == null ? "—" : usd(r.headline.avgCents)}/client`
|
|
3336
|
+
: "No complete month yet.",
|
|
3337
|
+
`Clients counted: ${r.coverage.counted} (${r.coverage.stripe} Stripe, ${r.coverage.deal} deal) · comp ${r.coverage.comp} · ${usd(r.coverage.countedCents)}/mo`,
|
|
3338
|
+
"",
|
|
3339
|
+
...r.periods.map(
|
|
3340
|
+
(p) =>
|
|
3341
|
+
`${p.month}${p.partial ? " (partial)" : ""}${p.estimated ? " ~" : ""} ${usd(p.startCents)} → ${usd(p.endCents)} NRR ${pct(p.nrr)} +new ${usd(p.moves.new)} +back ${usd(p.moves.reactivated)} +exp ${usd(p.moves.expansion)} −con ${usd(p.moves.contraction)} −churn ${usd(p.moves.churn)} ±reclassified ${usd(p.moves.reclassified)}`
|
|
3342
|
+
),
|
|
3343
|
+
].join("\n")
|
|
3344
|
+
);
|
|
3345
|
+
}
|
|
3328
3346
|
case "record-churn": {
|
|
3329
3347
|
// admin crm record-churn <account-id> <reason> [--note "…"] [--at ISO] [--mrr-cents N]
|
|
3330
3348
|
if (!id || !action) {
|
package/lib/commands/onboard.mjs
CHANGED
|
@@ -179,6 +179,8 @@ export const ARGS = {
|
|
|
179
179
|
propose_primitive_plan: {
|
|
180
180
|
domain: { positional: 0, label: "domain" },
|
|
181
181
|
retry: { flag: "retry", type: "bool" },
|
|
182
|
+
// Only these primitives, as a plan recipe allows: kernel,phantom,mailer,sequence.
|
|
183
|
+
primitives: { flag: "primitives", type: "list" },
|
|
182
184
|
},
|
|
183
185
|
propose_phantom_plan: {
|
|
184
186
|
client_domain: { positional: 0, label: "client-domain" },
|
package/lib/commands/site.mjs
CHANGED
|
@@ -58,6 +58,14 @@ const ARGS = {
|
|
|
58
58
|
unfold: { flag: "unfold", type: "bool" },
|
|
59
59
|
subtree: { flag: "subtree", type: "bool" },
|
|
60
60
|
since: { flag: "since" },
|
|
61
|
+
// The ranking's top n, or "all", instead of the attention policy's short list.
|
|
62
|
+
focus_size: {
|
|
63
|
+
value: (flags) => {
|
|
64
|
+
const raw = flags["focus-size"];
|
|
65
|
+
if (raw === undefined || raw === true) return undefined;
|
|
66
|
+
return raw === "all" ? "all" : Number(raw);
|
|
67
|
+
},
|
|
68
|
+
},
|
|
61
69
|
},
|
|
62
70
|
list_competitor_sites: {
|
|
63
71
|
since: { flag: "since" },
|