@audienti/cli 0.1.30 → 0.1.31

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/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@ All notable changes to the Audienti CLI are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.1.31] - 2026-08-10
8
+
9
+ ### Added
10
+
11
+ - Add `audienti setup play preflight` for checking LinkedIn connected-account readiness and returning direct setup, mapping, or edit URLs before an agent activates a play.
12
+ - Add `audienti analytics stages` for weekly or monthly stage conversion cohorts and current stage aging/overdue metrics.
13
+
7
14
  ## [0.1.30] - 2026-08-04
8
15
 
9
16
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audienti/cli",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "description": "Agent-first command-line client for Audienti.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api-client.js CHANGED
@@ -44,6 +44,10 @@ export class AudientiClient {
44
44
  return this.requestJson(accountPath(accountId, ["users"]));
45
45
  }
46
46
 
47
+ socialCookies(accountId, query = {}) {
48
+ return this.requestJson(accountPath(accountId, ["social_cookies"], query));
49
+ }
50
+
47
51
  userActivity(accountId, userId, query = {}) {
48
52
  return this.requestJson(accountPath(accountId, ["operations", "users", userId, "activity"], query));
49
53
  }
@@ -572,6 +576,10 @@ export class AudientiClient {
572
576
  return this.requestJson(accountPath(accountId, ["analytics", "dashboard"], query));
573
577
  }
574
578
 
579
+ analyticsStages(accountId, query = {}) {
580
+ return this.requestJson(accountPath(accountId, ["analytics", "stages"], query));
581
+ }
582
+
575
583
  createAnalyticsCohortList(accountId, body = {}) {
576
584
  return this.requestJson(accountPath(accountId, ["analytics", "cohort_lists"]), {
577
585
  method: "POST",
package/src/cli.js CHANGED
@@ -61,6 +61,7 @@ const TASKS_LIST_USAGE = "Usage: audienti tasks list [--status <open|completed>]
61
61
  const TASKS_ADD_USAGE = "Usage: audienti tasks add --title <text> --due <time> [--prospect <prsp_id>] [--list <list_id>] [--assigned-user <id|me>] [--notes <text>] [--json] [--account <acct_id>]";
62
62
  const TASKS_COMPLETE_USAGE = "Usage: audienti tasks complete <ptsk_id> [--json] [--account <acct_id>]";
63
63
  const USERS_ACTIVITY_USAGE = "Usage: audienti users activity [account_user_id|me] [--mode <actor|account_usage|related>] [--window <24h|7d|30d>] [--platform <linkedin|email|gmail>] [--query <text>] [--limit <n>] [--page <n>] [--json] [--account <acct_id>]";
64
+ const SETUP_PLAY_PREFLIGHT_USAGE = "Usage: audienti setup play preflight [--principal <account_user_id|email|name|me>] [--platform linkedin] [--json] [--account <acct_id>]";
64
65
  const OFFERS_SHOW_USAGE = "Usage: audienti offers show <offr_id> [--json] [--account <acct_id>]";
65
66
  const OFFERS_UPDATE_USAGE = "Usage: audienti offers update <offr_id> [--name <text>] [--description <text>] [--url <url>] [--json] [--account <acct_id>]";
66
67
  const OFFERS_DELETE_USAGE = "Usage: audienti offers delete <offr_id> --confirm <yes|true|Y|y> [--json] [--account <acct_id>]";
@@ -94,6 +95,7 @@ const ANALYTICS_PROSPECTS_USAGE = "Usage: audienti analytics prospects [--window
94
95
  const ANALYTICS_PROSPECTS_COHORT_ANALYSIS_USAGE = "Usage: audienti analytics prospects cohort-analysis [--weeks <n>] [--window 24h] [--motion <motn_id>] [--list <list_id>] [--provenance <source>] [--user <account_user_id|email|name|me>] [--json] [--account <acct_id>]";
95
96
  const ANALYTICS_USERS_USAGE = "Usage: audienti analytics users [--user <account_user_id|email|name|me>] [--window 30d | --start YYYY-MM-DD --end YYYY-MM-DD] [--cohort-start YYYY-MM-DD --cohort-end YYYY-MM-DD] [--motion <motn_id>] [--list <list_id>] [--provenance <source>] [--platform <linkedin|email|gmail>] [--json] [--account <acct_id>]";
96
97
  const ANALYTICS_DASHBOARD_USAGE = "Usage: audienti analytics dashboard [--cohort-start YYYY-MM-DD --cohort-end YYYY-MM-DD] [--play-tag <tag>] [--motion <motn_id>] [--list <list_id>] [--offer <offr_id>] [--icp <icp_id>] [--user <account_user_id|email|name|me>] [--json] [--account <acct_id>]";
98
+ const ANALYTICS_STAGES_USAGE = "Usage: audienti analytics stages [--interval weekly|monthly] [--cohort-start YYYY-MM-DD --cohort-end YYYY-MM-DD] [--play-tag <tag>] [--motion <motn_id>] [--list <list_id>] [--offer <offr_id>] [--icp <icp_id>] [--user <account_user_id|email|name|me>] [--json] [--account <acct_id>]";
97
99
  const ANALYTICS_COHORT_LIST_USAGE = "Usage: audienti analytics cohorts create-list --name <text> --start YYYY-MM-DD --end YYYY-MM-DD [--event connection_request_sent] [--user <account_user_id|email|name|me>] [--note-mode <any|with_note|blank>] [--motion <motn_id>] [--offer <offr_id>] [--icp <icp_id>] [--play-tag <tag>] [--json] [--account <acct_id>]";
98
100
  const TOOLS_LIST_USAGE = "Usage: audienti tools list [--json]";
99
101
  const TOOLS_LINKEDIN_REVIEW_USAGE = "Usage: audienti tools linkedin-review --url <linkedin_url> [--icp <icp_id>] [--json] [--account <acct_id>]";
@@ -189,6 +191,7 @@ async function dispatch(argv, context) {
189
191
  if (normalizedResource === "users" && action === "list") return usersList(rest, context, { accountOverride });
190
192
  if (normalizedResource === "users" && action === "select") return usersSelect(rest, context, { accountOverride });
191
193
  if (normalizedResource === "users" && action === "activity") return usersActivity(rest, context, { accountOverride });
194
+ if (normalizedResource === "setup" && action === "play" && rest[0] === "preflight") return setupPlayPreflight(rest.slice(1), context, { accountOverride });
192
195
  if (normalizedResource === "offers" && action === "list") return offersList(rest, context, { accountOverride });
193
196
  if (normalizedResource === "offers" && action === "show") return offersShow(rest, context, { accountOverride });
194
197
  if (normalizedResource === "offers" && action === "create") return offersCreate(rest, context, { accountOverride });
@@ -288,6 +291,7 @@ async function dispatch(argv, context) {
288
291
  if (normalizedResource === "analytics" && ["visibility", "visops"].includes(action)) return analyticsVisibility(rest, context, { accountOverride });
289
292
  if (normalizedResource === "analytics" && action === "content") return analyticsContent(rest, context, { accountOverride });
290
293
  if (normalizedResource === "analytics" && ["dashboard", "campaign", "campaigns"].includes(action)) return analyticsDashboard(rest, context, { accountOverride });
294
+ if (normalizedResource === "analytics" && action === "stages") return analyticsStages(rest, context, { accountOverride });
291
295
  if (normalizedResource === "analytics" && ["cohorts", "cohort"].includes(action)) return analyticsCohorts(rest, context, { accountOverride });
292
296
 
293
297
  throw new CommandError(usage(), { exitCode: resource ? 1 : 0 });
@@ -722,6 +726,24 @@ async function usersActivity(args, context, { accountOverride } = {}) {
722
726
  renderUserActivity(payload, context);
723
727
  }
724
728
 
729
+ async function setupPlayPreflight(args, context, { accountOverride } = {}) {
730
+ const { values, positionals } = parseCommandArgs(args, {
731
+ ...jsonOptions(),
732
+ principal: { type: "string" },
733
+ platform: { type: "string" }
734
+ });
735
+ if (positionals.length > 0) throw new CommandError(SETUP_PLAY_PREFLIGHT_USAGE);
736
+
737
+ const { client, accountId, config } = await requireAccountContext(context, { accountOverride });
738
+ const payload = await client.socialCookies(accountId, compactObject({
739
+ account_user_id: resolveAccountUserId(values.principal || "me", config, { accountOverride }),
740
+ platform: values.platform || "linkedin"
741
+ }));
742
+ if (values.json) return writeJson(context.stdout, payload);
743
+
744
+ renderSetupPlayPreflight(payload, context);
745
+ }
746
+
725
747
  async function offersList(args, context, { accountOverride } = {}) {
726
748
  const { values, positionals } = parseCommandArgs(args, jsonOptions());
727
749
  if (positionals.length > 0) throw new CommandError("Usage: audienti offers list [--json] [--account <acct_id>]");
@@ -2751,6 +2773,18 @@ async function analyticsDashboard(args, context, { accountOverride } = {}) {
2751
2773
  renderAnalyticsDashboard(payload, context);
2752
2774
  }
2753
2775
 
2776
+ async function analyticsStages(args, context, { accountOverride } = {}) {
2777
+ const { values, positionals } = parseCommandArgs(args, analyticsStagesOptions());
2778
+ if (positionals.length > 0) throw new CommandError(ANALYTICS_STAGES_USAGE);
2779
+ validateDatePair(values["cohort-start"], values["cohort-end"], "--cohort-start", "--cohort-end");
2780
+
2781
+ const { client, accountId, config } = await requireAccountContext(context, { accountOverride });
2782
+ const payload = await client.analyticsStages(accountId, analyticsStagesQuery(values, config, { accountOverride }));
2783
+ if (values.json) return writeJson(context.stdout, payload);
2784
+
2785
+ renderAnalyticsStages(payload, context);
2786
+ }
2787
+
2754
2788
  async function analyticsCohorts(args, context, { accountOverride } = {}) {
2755
2789
  const action = args[0];
2756
2790
  if (action !== "create-list") throw new CommandError(ANALYTICS_COHORT_LIST_USAGE);
@@ -3024,6 +3058,13 @@ function analyticsDashboardOptions() {
3024
3058
  };
3025
3059
  }
3026
3060
 
3061
+ function analyticsStagesOptions() {
3062
+ return {
3063
+ ...analyticsDashboardOptions(),
3064
+ interval: { type: "string" }
3065
+ };
3066
+ }
3067
+
3027
3068
  function analyticsCohortListOptions() {
3028
3069
  return {
3029
3070
  ...jsonOptions(),
@@ -3082,6 +3123,13 @@ function analyticsDashboardQuery(values, config = {}, { accountOverride } = {})
3082
3123
  });
3083
3124
  }
3084
3125
 
3126
+ function analyticsStagesQuery(values, config = {}, { accountOverride } = {}) {
3127
+ return compactObject({
3128
+ ...analyticsDashboardQuery(values, config, { accountOverride }),
3129
+ interval: values.interval
3130
+ });
3131
+ }
3132
+
3085
3133
  function validateDatePair(start, end, startFlag, endFlag) {
3086
3134
  if ((start && !end) || (!start && end)) {
3087
3135
  throw new CommandError(`${startFlag} and ${endFlag} must be provided together.`);
@@ -3875,6 +3923,51 @@ function renderUserActivity(payload, context) {
3875
3923
  }
3876
3924
  }
3877
3925
 
3926
+ function renderSetupPlayPreflight(payload, context) {
3927
+ const accountUser = payload?.account_user || {};
3928
+ const socialCookie = payload?.social_cookie || null;
3929
+ const urls = socialCookie?.urls || {};
3930
+
3931
+ writeLine(
3932
+ context.stdout,
3933
+ `Setup preflight: ${display(payload?.platform, "linkedin")} for ${display(accountUser.name || accountUser.email)} (${display(accountUser.id)})`
3934
+ );
3935
+ writeLine(context.stdout, `Status: ${payload?.ready ? "ready" : "blocked"} (${display(payload?.reason, "unknown")})`);
3936
+
3937
+ if (socialCookie) {
3938
+ writeLine(
3939
+ context.stdout,
3940
+ `Connected account: ${display(socialCookie.username || socialCookie.name)} (${display(socialCookie.prefix_id)})`
3941
+ );
3942
+ writeLine(context.stdout, `Connection status: ${display(socialCookie.status)}`);
3943
+ writeLine(context.stdout, `Mapped to account: ${socialCookie.accessible_to_account ? "yes" : "no"}`);
3944
+ writeLine(context.stdout, `Actionable: ${socialCookie.actionable ? "yes" : "no"}`);
3945
+ renderSetupAutomationSummary(socialCookie.automation || {}, context);
3946
+ } else {
3947
+ writeLine(context.stdout, "Connected account: none");
3948
+ }
3949
+
3950
+ if (!socialCookie && payload?.setup_url) {
3951
+ writeLine(context.stdout, `Setup URL: ${payload.setup_url}`);
3952
+ } else if (payload?.reason === "needs_account_access" && urls.mapping_url) {
3953
+ writeLine(context.stdout, `Mapping URL: ${urls.mapping_url}`);
3954
+ } else if (payload?.reason === "needs_reconnect" && urls.edit_url) {
3955
+ writeLine(context.stdout, `Edit URL: ${urls.edit_url}`);
3956
+ } else if (urls.edit_url) {
3957
+ writeLine(context.stdout, `Edit URL: ${urls.edit_url}`);
3958
+ }
3959
+ }
3960
+
3961
+ function renderSetupAutomationSummary(automation, context) {
3962
+ const summary = [
3963
+ `autopilot ${automation.autopilot_enabled ? "on" : "off"}`,
3964
+ `automatic sending ${automation.automatic_sending_enabled ? "on" : "off"}`,
3965
+ `connection requests ${automation.connection_request_autopilot_enabled ? "on" : "off"}`
3966
+ ];
3967
+
3968
+ writeLine(context.stdout, `Automation: ${summary.join(", ")}`);
3969
+ }
3970
+
3878
3971
  function renderCountRows(context, label, rows) {
3879
3972
  if (!Array.isArray(rows) || rows.length === 0) return;
3880
3973
 
@@ -5107,6 +5200,82 @@ function renderAnalyticsDashboard(payload, context) {
5107
5200
  writeCountTable(context, "Current pipeline stages", payload?.pipeline_stage_counts, ["STAGE", "COUNT"], countRow);
5108
5201
  }
5109
5202
 
5203
+ function renderAnalyticsStages(payload, context) {
5204
+ const conversionGrid = payload?.conversion_grid || {};
5205
+ writeLine(context.stdout, `Stage analytics (${display(payload?.cohort?.label, "selected cohort")})`);
5206
+ if (payload?.cohort) {
5207
+ writeLine(context.stdout, `Cohort: ${payload.cohort.start_date} to ${payload.cohort.end_date} (${display(payload.cohort.field, "account_prospects.created_at")})`);
5208
+ }
5209
+ writeDashboardFilters(payload, context);
5210
+ writeLine(context.stdout, `Conversion interval: ${display(conversionGrid.interval, "weekly")}`);
5211
+ writeLine(context.stdout, `Connection maturity window: ${display(conversionGrid.maturity_days, 21)} days`);
5212
+ writeStageConversionTable(conversionGrid, context);
5213
+ writeStageAgingTable(payload?.stage_aging, context);
5214
+ }
5215
+
5216
+ function writeStageConversionTable(conversionGrid, context) {
5217
+ const rows = Array.isArray(conversionGrid?.rows) ? conversionGrid.rows : [];
5218
+ const definitions = Array.isArray(conversionGrid?.stage_definitions) ? conversionGrid.stage_definitions : [];
5219
+ const columns = definitions.filter((definition) => definition?.key);
5220
+
5221
+ writeLine(context.stdout, "");
5222
+ writeLine(context.stdout, display(conversionGrid?.window_description, "Conversion cohorts"));
5223
+ if (rows.length === 0 || columns.length === 0) return writeLine(context.stdout, "None");
5224
+
5225
+ const headers = ["COHORT", ...columns.map((column) => display(column.label || column.key))];
5226
+ const tableRows = rows.map((row) => [
5227
+ display(row?.label),
5228
+ ...columns.map((column) => stageMetricValueLabel(row?.values?.[column.key]))
5229
+ ]);
5230
+
5231
+ writeAlignedTable(context, headers, tableRows);
5232
+ }
5233
+
5234
+ function writeStageAgingTable(stageAging, context) {
5235
+ const rows = Array.isArray(stageAging?.rows) ? stageAging.rows : [];
5236
+ writeLine(context.stdout, "");
5237
+ writeLine(context.stdout, "Stage Aging");
5238
+ if (rows.length === 0) return writeLine(context.stdout, "None");
5239
+
5240
+ const totals = stageAging?.totals || {};
5241
+ writeLine(
5242
+ context.stdout,
5243
+ `Totals: ${display(totals.current_count, 0)} current, ${display(totals.due_soon_count, 0)} due soon, ${display(totals.overdue_count, 0)} overdue (${percentageLabel(totals.overdue_rate)})`
5244
+ );
5245
+ writeAlignedTable(
5246
+ context,
5247
+ ["STAGE", "CURRENT", "DUE SOON", "OVERDUE", "OVERDUE %", "MED AGE", "OLDEST IDLE"],
5248
+ rows.map(stageAgingRow),
5249
+ { numericColumns: [false, true, true, true, true, true, true] }
5250
+ );
5251
+ }
5252
+
5253
+ function stageAgingRow(row) {
5254
+ return [
5255
+ display(row?.label || row?.key),
5256
+ display(row?.current_count, 0),
5257
+ display(row?.due_soon_count, 0),
5258
+ display(row?.overdue_count, 0),
5259
+ percentageLabel(row?.overdue_rate),
5260
+ dayCountLabel(row?.median_stage_age_days),
5261
+ dayCountLabel(row?.oldest_idle_days)
5262
+ ];
5263
+ }
5264
+
5265
+ function stageMetricValueLabel(value) {
5266
+ if (!value) return "-";
5267
+ if (value.kind === "count") return display(value.count, 0);
5268
+
5269
+ const numerator = display(value.numerator, 0);
5270
+ const denominator = display(value.denominator, 0);
5271
+ const suffix = value.maturing ? " maturing" : "";
5272
+ return `${numerator}/${denominator} ${percentageLabel(value.rate)}${suffix}`;
5273
+ }
5274
+
5275
+ function dayCountLabel(value) {
5276
+ return value === undefined || value === null || value === "" ? "-" : `${value}d`;
5277
+ }
5278
+
5110
5279
  function renderAnalyticsCohortList(payload, context) {
5111
5280
  const list = payload?.list || {};
5112
5281
  writeLine(context.stdout, `Created analytics cohort list ${display(list.name)} (${display(list.prefix_id)}).`);
@@ -5824,6 +5993,7 @@ const HELP_TOPICS = new Map([
5824
5993
  " audienti auth status",
5825
5994
  " audienti config list",
5826
5995
  " audienti update check",
5996
+ " audienti setup play preflight",
5827
5997
  " audienti users list",
5828
5998
  " audienti users select <account_user_id|email|name|me>",
5829
5999
  " audienti users activity [account_user_id|me]",
@@ -5914,6 +6084,7 @@ const HELP_TOPICS = new Map([
5914
6084
  " Analytics",
5915
6085
  " audienti analytics prospects --window 24h",
5916
6086
  " audienti analytics dashboard --play-tag <tag>",
6087
+ " audienti analytics stages --interval weekly",
5917
6088
  " audienti analytics cohorts create-list --name \"Blank note test\" --start 2026-07-20 --end 2026-07-20 --note-mode blank",
5918
6089
  " audienti analytics prospects cohort-analysis --weeks 4 --motion <motn_id>",
5919
6090
  " audienti analytics users --user me --window 30d",
@@ -5934,6 +6105,7 @@ const HELP_TOPICS = new Map([
5934
6105
  " Preview a campaign: audienti writer test-run <prsp_id>",
5935
6106
  " Analyze one motion: audienti motions analytics <motn_id>",
5936
6107
  " Count one campaign: audienti analytics dashboard --play-tag <tag>",
6108
+ " Compare stage rates: audienti analytics stages --interval weekly",
5937
6109
  " Audit your work: audienti analytics users --user me --window 30d",
5938
6110
  " Review reminders: audienti tasks list",
5939
6111
  "",
@@ -6114,6 +6286,53 @@ const HELP_TOPICS = new Map([
6114
6286
  " Saves accountId and accountName in local CLI config."
6115
6287
  ].join("\n")],
6116
6288
 
6289
+ ["setup", [
6290
+ "Usage:",
6291
+ " audienti setup play preflight [--principal <account_user_id|email|name|me>] [--platform linkedin] [--json]",
6292
+ "",
6293
+ "Status: implemented",
6294
+ "",
6295
+ "Purpose:",
6296
+ " Preflight account setup before an agent creates or activates an Audienti play.",
6297
+ "",
6298
+ "Commands:",
6299
+ " audienti setup play preflight Check connected-account readiness and direct setup URLs"
6300
+ ].join("\n")],
6301
+
6302
+ ["setup play", [
6303
+ "Usage:",
6304
+ " audienti setup play preflight [--principal <account_user_id|email|name|me>] [--platform linkedin] [--json]",
6305
+ "",
6306
+ "Status: implemented",
6307
+ "",
6308
+ "Purpose:",
6309
+ " Check the sender identity prerequisites for a new motion or play."
6310
+ ].join("\n")],
6311
+
6312
+ ["setup play preflight", [
6313
+ "Usage:",
6314
+ ` ${SETUP_PLAY_PREFLIGHT_USAGE.slice("Usage: ".length)}`,
6315
+ "",
6316
+ "Status: implemented",
6317
+ "",
6318
+ "Purpose:",
6319
+ " Verify that the selected principal has a connected account mapped to the current Audienti account before a play starts capture or outreach.",
6320
+ "",
6321
+ "Output shape:",
6322
+ " ready: true when the selected principal has a mapped, actionable platform account",
6323
+ " reason: ready | needs_setup | needs_account_access | needs_reconnect",
6324
+ " setup_url: direct operations URL for creating a connected account",
6325
+ " social_cookie.urls.mapping_url: owner URL for granting account access",
6326
+ " social_cookie.urls.edit_url: operations URL for reconnect/settings review when mapped",
6327
+ "",
6328
+ "Examples:",
6329
+ " audienti setup play preflight --principal me --platform linkedin",
6330
+ " audienti setup play preflight --principal 42 --json",
6331
+ "",
6332
+ "API:",
6333
+ " GET /api/v1/accounts/:account_id/social_cookies.json"
6334
+ ].join("\n")],
6335
+
6117
6336
  ["users", [
6118
6337
  "Usage:",
6119
6338
  " audienti users list [--json]",
@@ -8442,6 +8661,7 @@ const HELP_TOPICS = new Map([
8442
8661
  "Usage:",
8443
8662
  " audienti analytics prospects [--window 24h] [--cohort-start YYYY-MM-DD --cohort-end YYYY-MM-DD] [--motion <motn_id>] [--list <list_id>] [--user <account_user_id|email|name|me>] [--json]",
8444
8663
  " audienti analytics dashboard [--cohort-start YYYY-MM-DD --cohort-end YYYY-MM-DD] [--play-tag <tag>] [--motion <motn_id>] [--list <list_id>] [--json]",
8664
+ " audienti analytics stages [--interval weekly|monthly] [--cohort-start YYYY-MM-DD --cohort-end YYYY-MM-DD] [--play-tag <tag>] [--motion <motn_id>] [--list <list_id>] [--json]",
8445
8665
  " audienti analytics cohorts create-list --name <text> --start YYYY-MM-DD --end YYYY-MM-DD [--note-mode <any|with_note|blank>] [--user <account_user_id|email|name|me>] [--json]",
8446
8666
  " audienti analytics prospects cohort-analysis [--weeks <n>] [--window 24h] [--motion <motn_id>] [--list <list_id>] [--user <account_user_id|email|name|me>] [--json]",
8447
8667
  " audienti analytics users [--user <account_user_id|email|name|me>] [--window 30d | --start YYYY-MM-DD --end YYYY-MM-DD] [--cohort-start YYYY-MM-DD --cohort-end YYYY-MM-DD] [--motion <motn_id>] [--list <list_id>] [--platform <linkedin|email|gmail>] [--json]",
@@ -8458,6 +8678,7 @@ const HELP_TOPICS = new Map([
8458
8678
  " --motion <motn_id> For prospect and user analytics, filter AccountProspect.motion_id to one motion/play.",
8459
8679
  " --list <list_id> Filter analytics to prospects in one list, including analytics cohort lists.",
8460
8680
  " --play-tag <tag> For dashboard analytics, filter to motions/lists tagged with a campaign tag.",
8681
+ " --interval <weekly|monthly> For stages analytics, select the conversion cohort grain.",
8461
8682
  " --provenance <source> Optional lower-level AccountProspect.intake_source filter.",
8462
8683
  " --platform <linkedin|email|gmail> For user analytics, filter events.platform. --channel is accepted as an alias.",
8463
8684
  " cohort-analysis loops over recent weekly AccountProspect.created_at cohorts and compares their current stages.",
@@ -8497,6 +8718,35 @@ const HELP_TOPICS = new Map([
8497
8718
  " GET /api/v1/accounts/:account_id/analytics/dashboard.json"
8498
8719
  ].join("\n")],
8499
8720
 
8721
+ ["analytics stages", [
8722
+ "Usage:",
8723
+ ` ${ANALYTICS_STAGES_USAGE.slice("Usage: ".length)}`,
8724
+ "",
8725
+ "Status: implemented",
8726
+ "",
8727
+ "Purpose:",
8728
+ " Return the stages dashboard read model through the CLI, including weekly or monthly conversion cohorts and current stage aging.",
8729
+ "",
8730
+ "Options:",
8731
+ " --interval <weekly|monthly> Conversion cohort grain. Defaults to weekly.",
8732
+ " --cohort-start <YYYY-MM-DD> --cohort-end <YYYY-MM-DD> Select the AccountProspect.created_at cohort.",
8733
+ " --play-tag <tag> Filter to motions/lists tagged with a campaign tag. --tag is accepted as an alias.",
8734
+ " --motion <motn_id> Filter to one motion/play.",
8735
+ " --list <list_id> Filter to prospects in one list, including analytics cohort lists.",
8736
+ " --offer <offr_id> Filter to one offer.",
8737
+ " --icp <icp_id> Filter to one ICP.",
8738
+ " --user <account_user_id|email|name|me> Filter to prospects assigned to one account user.",
8739
+ "",
8740
+ "Output shape:",
8741
+ " conversion_grid.rows[]: period label, date bounds, totals_by_stage, and values keyed by stage metric",
8742
+ " conversion_grid.stage_definitions[]: stable metric keys and numerator/denominator stage definitions",
8743
+ " stage_aging.rows[]: current, due soon, overdue, age, and idle counts by current pipeline stage",
8744
+ " stage_aging.totals: rollup current, due soon, overdue, overdue_rate, and oldest_idle_days",
8745
+ "",
8746
+ "API:",
8747
+ " GET /api/v1/accounts/:account_id/analytics/stages.json"
8748
+ ].join("\n")],
8749
+
8500
8750
  ["analytics prospects", [
8501
8751
  "Usage:",
8502
8752
  ` ${ANALYTICS_PROSPECTS_USAGE.slice("Usage: ".length)}`,
@@ -8666,10 +8916,12 @@ const HELP_TOPICS = new Map([
8666
8916
  " audienti accounts select <acct_id>",
8667
8917
  " audienti users list",
8668
8918
  " audienti users select me",
8919
+ " audienti setup play preflight --principal me --platform linkedin",
8669
8920
  " audienti offers list",
8670
8921
  " audienti icps list",
8671
8922
  "",
8672
8923
  "2. Create a motion or play",
8924
+ " audienti setup play preflight --principal <account_user_id|me> --platform linkedin",
8673
8925
  " audienti motions create --payload <file.json>",
8674
8926
  " audienti motions clone <motn_id> --name \"New subset motion\"",
8675
8927
  " audienti motions move-prospects <source_motn_id> --target <target_motn_id> <prsp_id> [prsp_id...]",
@@ -8725,6 +8977,7 @@ const HELP_TOPICS = new Map([
8725
8977
  " audienti users activity me --window 7d",
8726
8978
  " audienti analytics prospects --window 24h",
8727
8979
  " audienti analytics dashboard --play-tag wine_campaign",
8980
+ " audienti analytics stages --interval weekly --play-tag wine_campaign",
8728
8981
  " audienti analytics cohorts create-list --name \"Connection requests 2026-07-20\" --start 2026-07-20 --end 2026-07-20",
8729
8982
  " audienti analytics users --user me --window 30d",
8730
8983
  " audienti analytics visibility --window 24h --user me",