@letterstory/cli 0.41.0 → 0.43.0

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/README.md CHANGED
@@ -384,6 +384,8 @@ letterstory admin crm signal <signal-id> qualify # link/create the account
384
384
  letterstory admin crm signal <signal-id> dismiss # the easy delete (kept, marked)
385
385
  letterstory admin crm accounts --stage demo_scheduled # with likelihood
386
386
  letterstory admin crm score <account-id> # recompute; prints the factors
387
+ letterstory admin crm slack-channels [--all] # Watchdog channels + the account each is linked to
388
+ letterstory admin crm link-slack <account-id> C0123ABC # the CRM's watch-only Slack link (`none` unlinks)
387
389
  ```
388
390
 
389
391
  Every trigger — Cal.com bookings, Circleback recordings, Letterhome dinner RSVPs, RB2B
package/lib/cli.mjs CHANGED
@@ -52,7 +52,7 @@ import {
52
52
  } from "./commands.mjs";
53
53
 
54
54
  // Keep in sync with cli/package.json.
55
- export const VERSION = "0.41.0";
55
+ export const VERSION = "0.43.0";
56
56
 
57
57
  // Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
58
58
  // can't accidentally swallow the id as --json's value.
@@ -561,10 +561,14 @@ Admin (STAFF key — --staff-key / LETTERSTORY_STAFF_API_KEY; acts before an org
561
561
  [--name …] [--email …] [--company …] [--domain …] [--linkedin …] [--detail …]
562
562
  | signal <id> qualify|dismiss|link [--account <id>]
563
563
  | accounts [--stage lead] [--q acme] | account <id> | score <account-id>
564
+ | slack-channels [--all] | link-slack <account-id> <channel-id|none>
565
+ | white-glove [--suggested] | set-white-glove <account-id> on|off [--note "…"]
564
566
  The staff CRM's pipeline: the inbound signal
565
567
  feed, recording a signal with no webhook
566
568
  (Bookface, a tweet like), qualifying or
567
569
  dismissing one, and the explained score.
570
+ White glove is a person's decision: the
571
+ desk only suggests ($1K+/mo post-sales).
568
572
  Needs crm:read (reads) / crm:write (writes).
569
573
  admin tuning-context [--q "<question>"] The experiment agent's context pack (JSON):
570
574
  the pipeline as deployed, levers, measures,
@@ -7,7 +7,7 @@
7
7
  // admin experiments list|show|new|update|note|started|spend|conclude|abandon … (tuning:experiment)
8
8
  // admin tuning-context [--q "<question>"] (tuning:experiment)
9
9
  // admin tuning-import <brief-results.json> [--experiment <id>] (tuning:experiment)
10
- // admin crm signals|record|signal|accounts|account|score … (crm:read / crm:write)
10
+ // admin crm signals|record|signal|accounts|account|score|slack-channels|link-slack|white-glove|set-white-glove … (crm:read / crm:write)
11
11
  // admin create-org --domain acme.com [--name "Acme"] [--test] [--no-key] [--key-name N]
12
12
  // [--capabilities a,b] [--onboard] [--about "…"] [--sitemap <url>]
13
13
  // [--cms <provider>] [--posts N] [--seers suggest|skip] [--no-visuals]
@@ -509,8 +509,68 @@ async function cmdAdminCrm(ctx) {
509
509
  ].join("\n")
510
510
  );
511
511
  }
512
+ case "slack-channels": {
513
+ const params = flagBool(flags.all) ? "?all=1" : "";
514
+ return out(await staffRequest(ctx, "GET", `${CRM}/slack-channels${params}`), (r) =>
515
+ r.channels.length
516
+ ? r.channels
517
+ .map(
518
+ (c) =>
519
+ `${c.id} #${c.name ?? "?"}${c.linkedAccountId ? ` → ${c.linkedAccountId}` : ""}`
520
+ )
521
+ .join("\n")
522
+ : "(no channels)"
523
+ );
524
+ }
525
+ case "link-slack": {
526
+ // admin crm link-slack <account-id> <channel-id|none> — the CRM's watch-only link (never a binding).
527
+ if (!id || !action) throw new CliError("Usage: admin crm link-slack <account-id> <channel-id|none>");
528
+ const r = await staffRequest(ctx, "PATCH", `${CRM}/accounts/${id}`, {
529
+ slack_channel_id: action === "none" ? null : action,
530
+ });
531
+ return out(r, () => (action === "none" ? "unlinked" : `linked ${action}`));
532
+ }
533
+ case "white-glove": {
534
+ // admin crm white-glove [--suggested] — the white-glove accounts, or the desk's suggestions.
535
+ const r = await staffRequest(ctx, "GET", `${CRM}/white-glove`);
536
+ if (flagBool(flags.suggested)) {
537
+ return out(r, (x) =>
538
+ x.suggested.length
539
+ ? x.suggested.map((s) => `${accountLine(s.account)}\n ${s.reason}`).join("\n")
540
+ : "(no suggestions)"
541
+ );
542
+ }
543
+ return out(r, (x) =>
544
+ x.accounts.length
545
+ ? x.accounts
546
+ .map(
547
+ (a) =>
548
+ `${accountLine(a)}${a.whiteGloveAt ? `\n since ${a.whiteGloveAt.slice(0, 10)}` : ""}${a.whiteGloveNote ? ` · ${a.whiteGloveNote}` : ""}`
549
+ )
550
+ .join("\n")
551
+ : "(no white-glove accounts)"
552
+ );
553
+ }
554
+ case "set-white-glove": {
555
+ // admin crm set-white-glove <account-id> on|off [--note "SLA 1h, weekly check-in"]
556
+ if (!id || !["on", "off"].includes(action)) {
557
+ throw new CliError(
558
+ 'Usage: admin crm set-white-glove <account-id> on|off [--note "SLA 1h, weekly check-in"]'
559
+ );
560
+ }
561
+ const note = flagStr(flags.note);
562
+ const r = await staffRequest(
563
+ ctx,
564
+ "PATCH",
565
+ `${CRM}/accounts/${id}`,
566
+ compact({ white_glove: action === "on", white_glove_note: note })
567
+ );
568
+ return out(r, () => (action === "on" ? "moved to white glove" : "left white glove"));
569
+ }
512
570
  default:
513
- throw new CliError("Usage: admin crm signals|record|signal|accounts|account|score … (see --help)");
571
+ throw new CliError(
572
+ "Usage: admin crm signals|record|signal|accounts|account|score|slack-channels|link-slack|white-glove|set-white-glove … (see --help)"
573
+ );
514
574
  }
515
575
  }
516
576
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.41.0",
3
+ "version": "0.43.0",
4
4
  "description": "Spin up and manage Letterstory phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",