@letterstory/cli 0.176.29 → 0.176.30

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
@@ -83,7 +83,7 @@ import {
83
83
  import { fetchParkedSections, withoutParked } from "./parked.mjs";
84
84
 
85
85
  // Matches cli/package.json; publish-cli.yml rewrites both to the published version.
86
- export const VERSION = "0.176.29";
86
+ export const VERSION = "0.176.30";
87
87
 
88
88
  /** The exit code for a gate refusal (CARD_REQUIRED, PAYMENT_FAILED, OUT_OF_TOKENS): 1 is a plain error. */
89
89
  export const EXIT_PAYMENT_REQUIRED = 3;
@@ -1247,6 +1247,11 @@ Admin (STAFF key — --staff-key / LETTERSTORY_STAFF_API_KEY; acts before an org
1247
1247
  | record-outcome <account-id> <kind> [--reason code] [--also a,b] [--cause …] [--notice-at ISO]
1248
1248
  [--at ISO] [--mrr-before N] [--mrr-after N] [--note "…"] [--save-offer …]
1249
1249
  [--save-result …] [--draft]
1250
+ | highlights [--account <id>] [--days N] [--limit N] customer love, newest first
1251
+ | highlights-scan [--days 7] [--offset N] [--limit N] [--min-strength 3] [--as-of ISO]
1252
+ a dry run over the calls: every moment the model finds, posting nothing
1253
+ | highlights-post --picks-file picks.json [--verdict keep|pass] post picks from a scan
1254
+ | highlights-reorder [--before-ts ts] [--limit N] repost every clip in call order (deletes old posts)
1250
1255
  | renewals [--days 90]
1251
1256
  | reach-outs [--day YYYY-MM-DD] [--fresh] the day's reach-outs, N of 5 (#crm-reminders' count)
1252
1257
  | replies [--days 14] replies nobody has answered yet, newest first
@@ -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|add|accounts|account|score|slack-channels|link-slack|white-glove|set-white-glove|churn|revenue|reach-outs|replies|record-churn|draft-churn-reason|undo-churn|outcomes|record-outcome|renewals|track-record|weekly-note|at-risk|expansion-signals|collections|delivery-flags|pulse|client-health|case|onboard|onboarding|retry-onboarding|onboard-plan|payment-link … (crm:read / crm:write)
17
+ // admin crm import|capture|outreach|signals|record|signal|add|accounts|account|score|slack-channels|link-slack|white-glove|set-white-glove|churn|revenue|reach-outs|replies|record-churn|draft-churn-reason|undo-churn|outcomes|record-outcome|highlights|highlights-scan|highlights-post|highlights-reorder|renewals|track-record|weekly-note|at-risk|expansion-signals|collections|delivery-flags|pulse|client-health|case|onboard|onboarding|retry-onboarding|onboard-plan|payment-link … (crm:read / crm:write)
18
18
  // admin spend summary|reconcile|pnl|margin|pricing|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)
@@ -3595,6 +3595,82 @@ async function cmdAdminCrm(ctx) {
3595
3595
  `${x.outcome.status === "draft" ? "drafted (a person confirms it)" : "recorded"}: ${outcomeLine(x.outcome)}`
3596
3596
  );
3597
3597
  }
3598
+ case "highlights": {
3599
+ // admin crm highlights [--account id] [--days N] [--limit N] — customer love, newest first.
3600
+ const params = new URLSearchParams(
3601
+ compact({ account_id: flagStr(flags.account), days: flagStr(flags.days), limit: flagStr(flags.limit) })
3602
+ );
3603
+ return out(await staffRequest(ctx, "GET", `${CRM}/highlights?${params}`), (r) =>
3604
+ r.highlights.length
3605
+ ? r.highlights
3606
+ .map(
3607
+ (h) =>
3608
+ `${h.occurredAt.slice(0, 10)} ${h.accountName ?? "?"} · ${h.speakerName ?? "?"} ${h.verdict ?? "undecided"} s${h.strength ?? "-"} "${h.quote.slice(0, 90)}" ${h.id}`
3609
+ )
3610
+ .join("\n")
3611
+ : "(no highlights)"
3612
+ );
3613
+ }
3614
+ case "highlights-scan": {
3615
+ // admin crm highlights-scan [--days 7] [--offset N] [--limit N] [--min-strength 3] [--as-of ISO]
3616
+ // A dry run: every moment the model finds, posting nothing. --json for picks to post.
3617
+ const params = new URLSearchParams(
3618
+ compact({
3619
+ days: flagStr(flags.days),
3620
+ offset: flagStr(flags.offset),
3621
+ limit: flagStr(flags.limit),
3622
+ min_strength: flagStr(flags["min-strength"]),
3623
+ as_of: flagStr(flags["as-of"]),
3624
+ })
3625
+ );
3626
+ return out(await staffRequest(ctx, "GET", `${CRM}/highlights/backfill?${params}`), (r) =>
3627
+ [
3628
+ ...r.calls.map((c) =>
3629
+ [
3630
+ `${c.occurredAt.slice(0, 10)} ${c.accountName ?? c.companyLabel ?? "?"} ${c.callName} ${c.skipped ?? ""}`,
3631
+ ...c.moments.map(
3632
+ (m) =>
3633
+ ` s${m.strength} ${m.alreadyPosted ? "(posted) " : ""}${m.speakerLabel ?? m.speaker} ${Math.round(m.startSec)}-${Math.round(m.endSec)}s: "${m.quote.slice(0, 100)}"`
3634
+ ),
3635
+ ].join("\n")
3636
+ ),
3637
+ r.nextOffset === null ? "(end)" : `more: --offset ${r.nextOffset} --as-of ${r.asOf}`,
3638
+ ].join("\n")
3639
+ );
3640
+ }
3641
+ case "highlights-post": {
3642
+ // admin crm highlights-post --picks-file picks.json [--verdict keep|pass]
3643
+ // picks.json: [{ meeting_id, speaker, quote, start_sec, end_sec, strength }], as a scan returned them.
3644
+ const picks = jsonFile(flags, "picks");
3645
+ if (!Array.isArray(picks))
3646
+ throw new CliError("Usage: admin crm highlights-post --picks-file picks.json [--verdict keep|pass]");
3647
+ const r = await staffPost(
3648
+ ctx,
3649
+ `${CRM}/highlights/backfill`,
3650
+ compact({ picks, verdict: flagStr(flags.verdict) })
3651
+ );
3652
+ return out(r, (x) =>
3653
+ x.results
3654
+ .map(
3655
+ (p) =>
3656
+ `${p.status}${p.clipped ? " (clip)" : ""} ${p.externalId}${p.error ? ` ${p.error}` : ""}`
3657
+ )
3658
+ .join("\n")
3659
+ );
3660
+ }
3661
+ case "highlights-reorder": {
3662
+ // admin crm highlights-reorder [--before-ts ts] [--limit N] — reposts every clip in call order, deleting old posts.
3663
+ const r = await staffPost(
3664
+ ctx,
3665
+ `${CRM}/highlights/reorder`,
3666
+ compact({ before_ts: flagStr(flags["before-ts"]), limit: flagNum(flags.limit) })
3667
+ );
3668
+ return out(
3669
+ r,
3670
+ (x) =>
3671
+ `reposted ${x.reposted}, ${x.remaining} to go${x.remaining ? ` (--before-ts ${x.beforeTs})` : ""}${x.stoppedOn ? `; stopped on ${x.stoppedOn.id}: ${x.stoppedOn.error}` : ""}`
3672
+ );
3673
+ }
3598
3674
  case "renewals": {
3599
3675
  // admin crm renewals [--days 90] — contracts renewing soon, and Stripe cancels scheduled.
3600
3676
  const params = new URLSearchParams(compact({ days: flagStr(flags.days) }));
@@ -3887,7 +3963,7 @@ async function cmdAdminCrm(ctx) {
3887
3963
  }
3888
3964
  default:
3889
3965
  throw new CliError(
3890
- "Usage: admin crm import|capture|outreach|signals|record|signal|accounts|account|score|slack-channels|link-slack|white-glove|set-white-glove|churn|revenue|reach-outs|replies|record-churn|draft-churn-reason|undo-churn|outcomes|record-outcome|renewals|track-record|weekly-note|at-risk|expansion-signals|collections|delivery-flags|pulse|client-health|case|onboard|onboarding|retry-onboarding|onboard-plan|prospect-org|prospect-orgs|prospect-org-repost|payment-link … (see --help)"
3966
+ "Usage: admin crm import|capture|outreach|signals|record|signal|accounts|account|score|slack-channels|link-slack|white-glove|set-white-glove|churn|revenue|reach-outs|replies|record-churn|draft-churn-reason|undo-churn|outcomes|record-outcome|highlights|highlights-scan|highlights-post|highlights-reorder|renewals|track-record|weekly-note|at-risk|expansion-signals|collections|delivery-flags|pulse|client-health|case|onboard|onboarding|retry-onboarding|onboard-plan|prospect-org|prospect-orgs|prospect-org-repost|payment-link … (see --help)"
3891
3967
  );
3892
3968
  }
3893
3969
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.176.29",
3
+ "version": "0.176.30",
4
4
  "description": "Spin up and manage Letter phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",