@audienti/cli 0.1.32 → 0.1.34

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,24 @@ All notable changes to the Audienti CLI are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.1.34] - 2026-08-17
8
+
9
+ ### Added
10
+
11
+ - Add `audienti icps update <icp_id> --payload <file.json>` for rich ICP facet patches using the same payload shape as ICP creation.
12
+ - Surface executable motion configuration validity in `audienti motions status`, including missing outbound signals and missing LOPA tracked profile URLs.
13
+ - Add outbound motion signal configuration to `audienti motions create --payload` and `audienti motions update <motn_id> --payload <file.json>`, including scoped company/person/both signal rows.
14
+
15
+ ### Changed
16
+
17
+ - Default omitted inbound motion channels to LinkedIn-only when motions are created through the CLI/API contract; Reddit remains explicit opt-in.
18
+
19
+ ## [0.1.33] - 2026-08-13
20
+
21
+ ### Added
22
+
23
+ - Add `audienti motions quick-start --url <company_url>` for URL-generated quick-start drafts, with optional `--wait --confirm` motion setup and discovery launch.
24
+
7
25
  ## [0.1.32] - 2026-08-11
8
26
 
9
27
  ### Added
package/README.md CHANGED
@@ -139,8 +139,10 @@ audienti operator next --plan
139
139
  audienti writer test-run <prsp_id>
140
140
  audienti motions analytics <motn_id>
141
141
  audienti motions run-discovery <motn_id>
142
+ audienti motions quick-start --url https://example.com --wait --confirm
142
143
  audienti motions update <motn_id> --status paused
143
144
  audienti motions update <motn_id> --own-post-engagement true
145
+ audienti motions update <motn_id> --payload motion-signals.json
144
146
  audienti motions activate <motn_id>
145
147
  audienti motions delete <motn_id> --confirm yes
146
148
  audienti content programs
@@ -248,6 +250,36 @@ used by the operator surface:
248
250
  audienti motions run-discovery <motn_id>
249
251
  ```
250
252
 
253
+ To check whether a motion has executable configuration before preparing,
254
+ activating, or running discovery:
255
+
256
+ ```bash
257
+ audienti motions status <motn_id>
258
+ audienti motions status <motn_id> --json
259
+ ```
260
+
261
+ The JSON response includes `executable_configuration.valid` plus a reason when
262
+ the config is incomplete, such as missing outbound signals or missing LOPA
263
+ tracked profile URLs.
264
+
265
+ To create and launch a quick-start motion from a company URL:
266
+
267
+ ```bash
268
+ audienti motions quick-start --url https://example.com --wait --confirm
269
+ ```
270
+
271
+ To create or update an ICP from a rich JSON payload:
272
+
273
+ ```bash
274
+ audienti icps create --payload icp.json
275
+ audienti icps update <icp_id> --payload icp-patch.json
276
+ ```
277
+
278
+ `icps update --payload` accepts the same human-readable facet keys as create.
279
+ Supplied facet collections, such as `company_sizes_attributes`, replace that
280
+ collection in place; omitted fields and collections remain unchanged. Invalid
281
+ lookup values fail server-side without partially applying the patch.
282
+
251
283
  To audit one account user's outbound actions, optionally narrowed to one motion
252
284
  and one AccountProspect.created_at cohort:
253
285
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audienti/cli",
3
- "version": "0.1.32",
3
+ "version": "0.1.34",
4
4
  "description": "Agent-first command-line client for Audienti.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -100,6 +100,7 @@ audienti prospects unlock <prsp_id> --json
100
100
  audienti users activity me --window 7d --json
101
101
  audienti prospects import-batch --file prospects.csv --motion <motn_id> --assigned-user me --json
102
102
  audienti lists create --name "Target list" --json
103
+ audienti motions quick-start --url https://example.com --wait --confirm --json
103
104
  audienti motions update <motn_id> --status paused --json
104
105
  audienti motions activate <motn_id> --json
105
106
  audienti motions delete <motn_id> --confirm yes --json
package/src/api-client.js CHANGED
@@ -375,6 +375,24 @@ export class AudientiClient {
375
375
  });
376
376
  }
377
377
 
378
+ createQuickStart(accountId, body) {
379
+ return this.requestJson(accountPath(accountId, ["quick_start"]), {
380
+ method: "POST",
381
+ body
382
+ });
383
+ }
384
+
385
+ quickStart(accountId, draftId) {
386
+ return this.requestJson(accountPath(accountId, ["quick_start", draftId]));
387
+ }
388
+
389
+ confirmQuickStart(accountId, draftId, body = {}) {
390
+ return this.requestJson(accountPath(accountId, ["quick_start", draftId, "confirm"]), {
391
+ method: "POST",
392
+ body
393
+ });
394
+ }
395
+
378
396
  motionProspects(accountId, motionId, query = {}) {
379
397
  return this.requestJson(accountPath(accountId, ["motions", motionId, "prospects"], query));
380
398
  }
package/src/cli.js CHANGED
@@ -72,7 +72,7 @@ const OFFERS_DELETE_USAGE = "Usage: audienti offers delete <offr_id> --confirm <
72
72
  const WRITER_TEST_RUN_USAGE = "Usage: audienti writer test-run <prsp_id> [--json] [--mode <plan|report|step>] [--branch <both|no-accept|accepted>] [--step <step_key|row_number>] [--report <rprt_id>] [--no-wait] [--timeout-seconds <n>] [--poll-interval-seconds <n>] [--account <acct_id>]";
73
73
  const WRITER_TEST_RUN_SHOW_USAGE = "Usage: audienti writer test-run show <prsp_id> <rprt_id> [--json] [--account <acct_id>]";
74
74
  const MOTIONS_ANALYTICS_USAGE = "Usage: audienti motions analytics <motn_id> [--window 30d] [--json] [--account <acct_id>]";
75
- const MOTIONS_UPDATE_USAGE = "Usage: audienti motions update <motn_id> [--status <draft|preparing|active|paused|archived>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>] [--json] [--account <acct_id>]";
75
+ const MOTIONS_UPDATE_USAGE = "Usage: audienti motions update <motn_id> ([--status <draft|preparing|active|paused|archived>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>] | --payload <file.json>) [--json] [--account <acct_id>]";
76
76
  const CONTENT_PROGRAMS_USAGE = "Usage: audienti content programs [--user <account_user_id|email|name|me>] [--json] [--account <acct_id>]";
77
77
  const CONTENT_PLAN_USAGE = "Usage: audienti content plan <cprg_id> [--week <n>] [--due] [--json] [--account <acct_id>]";
78
78
  const CONTENT_SHOW_USAGE = "Usage: audienti content show <cpwi_id> [--json] [--account <acct_id>]";
@@ -89,8 +89,9 @@ const MOTIONS_DELETE_USAGE = "Usage: audienti motions delete <motn_id> --confirm
89
89
  const MOTIONS_CLONE_USAGE = "Usage: audienti motions clone <motn_id> --name <text> [--json] [--account <acct_id>]";
90
90
  const MOTIONS_MOVE_PROSPECTS_USAGE = "Usage: audienti motions move-prospects <source_motn_id> --target <target_motn_id> <prsp_id> [prsp_id...] [--json] [--account <acct_id>]";
91
91
  const MOTIONS_RUN_DISCOVERY_USAGE = "Usage: audienti motions run-discovery <motn_id> [--target-count <n>] [--json] [--account <acct_id>]";
92
+ const MOTIONS_QUICK_START_USAGE = "Usage: audienti motions quick-start --url <company_url> [--principal <account_user_id|me>] [--feedback <text>] [--offer-type <type>] [--force] [--confirm] [--wait] [--timeout-seconds <n>] [--poll-interval-seconds <n>] [--json] [--account <acct_id>]";
92
93
  const ICPS_SHOW_USAGE = "Usage: audienti icps show <icp_id> [--json] [--account <acct_id>]";
93
- const ICPS_UPDATE_USAGE = "Usage: audienti icps update <icp_id> [--name <text>] [--notes <text>] [--discovery-keyword <text>] [--tags <tag[,tag...]>] [--json] [--account <acct_id>]";
94
+ const ICPS_UPDATE_USAGE = "Usage: audienti icps update <icp_id> ([--name <text>] [--notes <text>] [--discovery-keyword <text>] [--tags <tag[,tag...]>] | --payload <file.json>) [--json] [--account <acct_id>]";
94
95
  const ICPS_ADD_TAG_USAGE = "Usage: audienti icps add-tag <icp_id> <tag> [--json] [--account <acct_id>]";
95
96
  const ICPS_REMOVE_TAG_USAGE = "Usage: audienti icps remove-tag <icp_id> <tag> [--json] [--account <acct_id>]";
96
97
  const LISTS_ADD_TAG_USAGE = "Usage: audienti lists add-tag <list_id> <tag> [--json] [--account <acct_id>]";
@@ -248,6 +249,7 @@ async function dispatch(argv, context) {
248
249
  if (normalizedResource === "motions" && action === "clone") return motionsClone(rest, context, { accountOverride });
249
250
  if (normalizedResource === "motions" && action === "move-prospects") return motionsMoveProspects(rest, context, { accountOverride });
250
251
  if (normalizedResource === "motions" && action === "run-discovery") return motionsRunDiscovery(rest, context, { accountOverride });
252
+ if (normalizedResource === "motions" && action === "quick-start") return motionsQuickStart(rest, context, { accountOverride });
251
253
  if (normalizedResource === "content" && action === "programs") return contentPrograms(rest, context, { accountOverride });
252
254
  if (normalizedResource === "content" && action === "plan") return contentPlan(rest, context, { accountOverride });
253
255
  if (normalizedResource === "content" && action === "show") return contentShow(rest, context, { accountOverride });
@@ -1028,25 +1030,19 @@ async function icpsCreate(args, context, { accountOverride } = {}) {
1028
1030
  async function icpsUpdate(args, context, { accountOverride } = {}) {
1029
1031
  const { values, positionals } = parseCommandArgs(args, {
1030
1032
  ...jsonOptions(),
1033
+ payload: { type: "string" },
1031
1034
  name: { type: "string" },
1032
1035
  notes: { type: "string" },
1033
1036
  tags: { type: "string" },
1034
1037
  "discovery-keyword": { type: "string" }
1035
1038
  });
1036
- const hasUpdateField = values.name || values.notes !== undefined || values.tags !== undefined || values["discovery-keyword"] !== undefined;
1039
+ const hasUpdateField = values.payload || icpSimpleFieldsPresent(values);
1037
1040
  if (positionals.length !== 1 || !hasUpdateField) {
1038
1041
  throw new CommandError(ICPS_UPDATE_USAGE);
1039
1042
  }
1040
1043
 
1041
1044
  const { client, accountId } = await requireAccountContext(context, { accountOverride });
1042
- const icp = await client.updateIcp(accountId, positionals[0], {
1043
- icp: compactObject({
1044
- name: values.name,
1045
- notes: values.notes,
1046
- discovery_keyword: values["discovery-keyword"],
1047
- tags: values.tags !== undefined ? tagList(values.tags) : undefined
1048
- })
1049
- });
1045
+ const icp = await client.updateIcp(accountId, positionals[0], { icp: await icpUpdatePayload(values) });
1050
1046
  if (values.json) return writeJson(context.stdout, icp);
1051
1047
 
1052
1048
  writeLine(context.stdout, `Updated ICP ${display(icp?.name)} (${display(icp?.prefix_id)}).`);
@@ -1543,6 +1539,55 @@ async function motionsRunDiscovery(args, context, { accountOverride } = {}) {
1543
1539
  renderMotionDiscoveryRun(payload, context);
1544
1540
  }
1545
1541
 
1542
+ async function motionsQuickStart(args, context, { accountOverride } = {}) {
1543
+ const { values, positionals } = parseCommandArgs(args, {
1544
+ ...jsonOptions(),
1545
+ url: { type: "string" },
1546
+ principal: { type: "string" },
1547
+ feedback: { type: "string" },
1548
+ "offer-type": { type: "string" },
1549
+ force: { type: "boolean" },
1550
+ confirm: { type: "boolean" },
1551
+ wait: { type: "boolean" },
1552
+ "timeout-seconds": { type: "string" },
1553
+ "poll-interval-seconds": { type: "string" }
1554
+ });
1555
+ if (positionals.length > 0 || !values.url) throw new CommandError(MOTIONS_QUICK_START_USAGE);
1556
+
1557
+ const timeoutSeconds = normalizeOptionalPositiveInteger(values["timeout-seconds"], "--timeout-seconds") || DEFAULT_LOOKUP_TIMEOUT_SECONDS;
1558
+ const pollIntervalSeconds = normalizeOptionalPositiveInteger(values["poll-interval-seconds"], "--poll-interval-seconds") || DEFAULT_LOOKUP_POLL_INTERVAL_SECONDS;
1559
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
1560
+ const requestBody = {
1561
+ quick_start: compactObject({
1562
+ company_url: values.url,
1563
+ principal_account_user_id: values.principal,
1564
+ feedback: values.feedback,
1565
+ offer_type: values["offer-type"],
1566
+ force: values.force || undefined
1567
+ })
1568
+ };
1569
+
1570
+ let draft = await client.createQuickStart(accountId, requestBody);
1571
+ if (values.wait && !quickStartReady(draft)) {
1572
+ draft = await waitForQuickStartDraft(client, accountId, draft, context, { timeoutSeconds, pollIntervalSeconds });
1573
+ }
1574
+
1575
+ if (values.confirm) {
1576
+ if (!quickStartReady(draft)) {
1577
+ throw new CommandError(`Quick-start draft ${display(draft?.id)} is ${display(draft?.status)}. Re-run with --wait or confirm after it is ready.`);
1578
+ }
1579
+
1580
+ const payload = await client.confirmQuickStart(accountId, draft.id, {});
1581
+ if (values.json) return writeJson(context.stdout, payload);
1582
+
1583
+ renderQuickStartConfirmation(payload, context);
1584
+ return;
1585
+ }
1586
+
1587
+ if (values.json) return writeJson(context.stdout, draft);
1588
+ renderQuickStartDraft(draft, context);
1589
+ }
1590
+
1546
1591
  async function motionsAnalytics(args, context, { accountOverride } = {}) {
1547
1592
  const { values, positionals } = parseCommandArgs(args, {
1548
1593
  ...jsonOptions(),
@@ -1632,7 +1677,7 @@ async function motionsCreate(args, context, { accountOverride } = {}) {
1632
1677
  }
1633
1678
 
1634
1679
  const { client, accountId } = await requireAccountContext(context, { accountOverride });
1635
- const payload = await readJsonPayload(values.payload);
1680
+ const payload = normalizeMotionCreatePayload(await readJsonPayload(values.payload));
1636
1681
  const created = await client.createMotion(accountId, { motion: payload });
1637
1682
  if (values.json) return writeJson(context.stdout, created);
1638
1683
 
@@ -1640,6 +1685,14 @@ async function motionsCreate(args, context, { accountOverride } = {}) {
1640
1685
  renderMotion(created, context);
1641
1686
  }
1642
1687
 
1688
+ function normalizeMotionCreatePayload(payload) {
1689
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return payload;
1690
+ if (String(payload.kind || "").trim().toLowerCase() !== "inbound") return payload;
1691
+ if (Object.prototype.hasOwnProperty.call(payload, "inbound_channels")) return payload;
1692
+
1693
+ return { ...payload, inbound_channels: ["linkedin"] };
1694
+ }
1695
+
1643
1696
  async function motionsDelete(args, context, { accountOverride } = {}) {
1644
1697
  const { values, positionals } = parseCommandArgs(args, {
1645
1698
  ...jsonOptions(),
@@ -1660,30 +1713,44 @@ async function motionsDelete(args, context, { accountOverride } = {}) {
1660
1713
  async function motionsUpdate(args, context, { accountOverride } = {}) {
1661
1714
  const { values, positionals } = parseCommandArgs(args, {
1662
1715
  ...jsonOptions(),
1716
+ payload: { type: "string" },
1663
1717
  status: { type: "string" },
1664
1718
  tags: { type: "string" },
1665
1719
  "own-post-engagement": { type: "string" }
1666
1720
  });
1667
- const hasUpdateField = values.status || values.tags !== undefined || values["own-post-engagement"] !== undefined;
1721
+ const hasUpdateField = values.payload || motionSimpleFieldsPresent(values);
1668
1722
  if (positionals.length !== 1 || !hasUpdateField) {
1669
1723
  throw new CommandError(MOTIONS_UPDATE_USAGE);
1670
1724
  }
1671
1725
 
1672
- const normalizedStatus = normalizeMotionStatus(values.status);
1673
- const motionAttributes = compactObject({
1674
- status: normalizedStatus,
1675
- play_tags: values.tags !== undefined ? tagList(values.tags) : undefined,
1676
- own_post_engagement: values["own-post-engagement"] !== undefined ? parseBooleanString(values["own-post-engagement"], "--own-post-engagement") : undefined
1677
- });
1678
-
1679
1726
  const { client, accountId } = await requireAccountContext(context, { accountOverride });
1680
- const motion = await client.updateMotion(accountId, positionals[0], { motion: motionAttributes });
1727
+ const motion = await client.updateMotion(accountId, positionals[0], { motion: await motionUpdatePayload(values) });
1681
1728
  if (values.json) return writeJson(context.stdout, motion);
1682
1729
 
1683
1730
  writeLine(context.stdout, `Updated motion ${display(motion?.name)} (${display(motion?.prefix_id)}).`);
1684
1731
  renderMotion(motion, context);
1685
1732
  }
1686
1733
 
1734
+ async function motionUpdatePayload(values) {
1735
+ if (values.payload) {
1736
+ if (motionSimpleFieldsPresent(values)) {
1737
+ throw new CommandError("Choose one motion input mode: either --payload <file.json> or the simple --status/--tags/--own-post-engagement flags.");
1738
+ }
1739
+
1740
+ return readJsonPayload(values.payload);
1741
+ }
1742
+
1743
+ return compactObject({
1744
+ status: normalizeMotionStatus(values.status),
1745
+ play_tags: values.tags !== undefined ? tagList(values.tags) : undefined,
1746
+ own_post_engagement: values["own-post-engagement"] !== undefined ? parseBooleanString(values["own-post-engagement"], "--own-post-engagement") : undefined
1747
+ });
1748
+ }
1749
+
1750
+ function motionSimpleFieldsPresent(values) {
1751
+ return Boolean(values.status) || values.tags !== undefined || values["own-post-engagement"] !== undefined;
1752
+ }
1753
+
1687
1754
  async function motionsStatusShortcut(action, args, context, { accountOverride } = {}) {
1688
1755
  const usage = `Usage: audienti motions ${action} <motn_id> [--json] [--account <acct_id>]`;
1689
1756
  const { values, positionals } = parseCommandArgs(args, jsonOptions());
@@ -3524,7 +3591,7 @@ function filterRecordsByTag(records, value, field) {
3524
3591
 
3525
3592
  async function icpCreatePayload(values) {
3526
3593
  if (values.payload) {
3527
- if (values.name || values.notes || values.tags !== undefined || values["discovery-keyword"]) {
3594
+ if (icpSimpleFieldsPresent(values)) {
3528
3595
  throw new CommandError("Choose one ICP input mode: either --payload <file.json> or the simple --name/--notes/--discovery-keyword/tags flags.");
3529
3596
  }
3530
3597
 
@@ -3543,6 +3610,27 @@ async function icpCreatePayload(values) {
3543
3610
  });
3544
3611
  }
3545
3612
 
3613
+ async function icpUpdatePayload(values) {
3614
+ if (values.payload) {
3615
+ if (icpSimpleFieldsPresent(values)) {
3616
+ throw new CommandError("Choose one ICP input mode: either --payload <file.json> or the simple --name/--notes/--discovery-keyword/tags flags.");
3617
+ }
3618
+
3619
+ return readJsonPayload(values.payload);
3620
+ }
3621
+
3622
+ return compactObject({
3623
+ name: values.name,
3624
+ notes: values.notes,
3625
+ tags: values.tags !== undefined ? tagList(values.tags) : undefined,
3626
+ discovery_keyword: values["discovery-keyword"]
3627
+ });
3628
+ }
3629
+
3630
+ function icpSimpleFieldsPresent(values) {
3631
+ return Boolean(values.name) || values.notes !== undefined || values.tags !== undefined || values["discovery-keyword"] !== undefined;
3632
+ }
3633
+
3546
3634
  async function prospectNotePayload(values, { forcedType, usageText } = {}) {
3547
3635
  const engagementKey = values["engagement-type"] || values["engagement-key"];
3548
3636
 
@@ -4272,6 +4360,7 @@ function renderMotion(motion, context) {
4272
4360
  writeLine(context.stdout, `Motion: ${display(motion?.name)} (${display(motion?.prefix_id)})`);
4273
4361
  writeLine(context.stdout, `Status: ${display(motion?.status)}`);
4274
4362
  writeLine(context.stdout, `Kind: ${display(motion?.kind)}`);
4363
+ renderExecutableConfiguration(motion?.executable_configuration, context);
4275
4364
  if (motion?.offer?.name) writeLine(context.stdout, `Offer: ${motion.offer.name} (${display(motion.offer.prefix_id)})`);
4276
4365
  if (motion?.icp?.name) writeLine(context.stdout, `ICP: ${motion.icp.name} (${display(motion.icp.prefix_id)})`);
4277
4366
  if (motion?.list?.name) writeLine(context.stdout, `List: ${motion.list.name} (${display(motion.list.prefix_id)})`);
@@ -4342,10 +4431,23 @@ function renderMotionStatus(status, context) {
4342
4431
  writeLine(context.stdout, `Motion: ${display(status?.name)} (${display(status?.prefix_id)})`);
4343
4432
  writeLine(context.stdout, `State: ${display(status?.state)}`);
4344
4433
  writeLine(context.stdout, `Reason: ${display(status?.reason_label || status?.reason_key)}`);
4434
+ renderExecutableConfiguration(status?.executable_configuration, context);
4345
4435
  if (status?.description) writeLine(context.stdout, status.description);
4346
4436
  if (status?.action?.label) writeLine(context.stdout, `Action: ${status.action.label}`);
4347
4437
  }
4348
4438
 
4439
+ function renderExecutableConfiguration(config, context) {
4440
+ if (!config || typeof config !== "object") return;
4441
+
4442
+ writeLine(context.stdout, `Valid config: ${config.valid ? "yes" : "no"}`);
4443
+ if (!config.valid && (config.reason_label || config.reason_key)) {
4444
+ writeLine(context.stdout, `Config reason: ${display(config.reason_label || config.reason_key)}`);
4445
+ }
4446
+ if (!config.valid && config.description) {
4447
+ writeLine(context.stdout, `Config detail: ${config.description}`);
4448
+ }
4449
+ }
4450
+
4349
4451
  function renderProspects(payload, context, { wide = false, profiles = false } = {}) {
4350
4452
  const prospects = Array.isArray(payload?.prospects) ? payload.prospects : [];
4351
4453
  if (prospects.length === 0) return writeLine(context.stdout, "No prospects found.");
@@ -5324,6 +5426,45 @@ function renderMotionDiscoveryRun(payload, context) {
5324
5426
  writeLine(context.stdout, `Target count: ${display(payload?.target_count)}`);
5325
5427
  }
5326
5428
 
5429
+ function renderQuickStartDraft(draft, context) {
5430
+ writeLine(context.stdout, `Quick-start draft ${display(draft?.id)} is ${display(draft?.status)}.`);
5431
+ writeLine(context.stdout, `URL: ${display(draft?.normalized_url)}`);
5432
+ if (draft?.principal_account_user) writeLine(context.stdout, `Principal: ${accountUserLabel(draft.principal_account_user)}`);
5433
+ if (draft?.expires_at) writeLine(context.stdout, `Expires: ${draft.expires_at}`);
5434
+ if (draft?.error) writeLine(context.stdout, `Error: ${draft.error}`);
5435
+ if (quickStartReady(draft)) writeLine(context.stdout, `Confirm: audienti motions quick-start --url ${draft.normalized_url} --confirm`);
5436
+ }
5437
+
5438
+ function renderQuickStartConfirmation(payload, context) {
5439
+ const motion = payload?.motion || {};
5440
+ writeLine(context.stdout, `Quick-start motion ${display(entityLabel(motion) || motion.prefix_id)} ${payload?.reused ? "reused" : "created"}.`);
5441
+ writeLine(context.stdout, `Motion status: ${display(motion.status)}`);
5442
+ writeLine(context.stdout, `Reservation: ${display(payload?.reservation?.status)} / ${display(payload?.reservation?.launch_status)}`);
5443
+ if (payload?.reservation?.launch_reason) writeLine(context.stdout, `Launch reason: ${payload.reservation.launch_reason}`);
5444
+ }
5445
+
5446
+ function quickStartReady(draft) {
5447
+ return draft?.ready === true || draft?.status === "ready";
5448
+ }
5449
+
5450
+ function quickStartTerminal(draft) {
5451
+ return quickStartReady(draft) || draft?.status === "failed";
5452
+ }
5453
+
5454
+ async function waitForQuickStartDraft(client, accountId, initialDraft, context, { timeoutSeconds, pollIntervalSeconds }) {
5455
+ let draft = initialDraft;
5456
+ const deadline = context.now().getTime() + timeoutSeconds * 1000;
5457
+
5458
+ while (!quickStartTerminal(draft)) {
5459
+ if (context.now().getTime() >= deadline) return draft;
5460
+
5461
+ await context.sleep(pollIntervalSeconds * 1000);
5462
+ draft = await client.quickStart(accountId, draft.id);
5463
+ }
5464
+
5465
+ return draft;
5466
+ }
5467
+
5327
5468
  function renderAnalyticsDashboard(payload, context) {
5328
5469
  writeLine(context.stdout, `Dashboard analytics (${display(payload?.cohort?.label, "selected cohort")})`);
5329
5470
  if (payload?.cohort) {
@@ -6145,9 +6286,11 @@ const HELP_TOPICS = new Map([
6145
6286
  " audienti motions show <motn_id>",
6146
6287
  " audienti motions analytics <motn_id>",
6147
6288
  " audienti motions run-discovery <motn_id>",
6289
+ " audienti motions quick-start --url <company_url> --confirm --wait",
6148
6290
  " audienti motions prospects <motn_id>",
6149
6291
  " audienti motions create --payload <file.json>",
6150
6292
  " audienti motions update <motn_id> [--status <state>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>]",
6293
+ " audienti motions update <motn_id> --payload <file.json>",
6151
6294
  " audienti motions add-tag <motn_id> <tag>",
6152
6295
  " audienti motions remove-tag <motn_id> <tag>",
6153
6296
  " audienti motions activate <motn_id>",
@@ -6202,7 +6345,7 @@ const HELP_TOPICS = new Map([
6202
6345
  " audienti offers delete <offr_id> --confirm <yes|true|Y|y>",
6203
6346
  " audienti icps list [--tag <tag>]",
6204
6347
  " audienti icps show <icp_id>",
6205
- " audienti icps update <icp_id> [--tags <tag[,tag...]>]",
6348
+ " audienti icps update <icp_id> [--tags <tag[,tag...]> | --payload <file.json>]",
6206
6349
  " audienti icps add-tag <icp_id> <tag>",
6207
6350
  " audienti icps remove-tag <icp_id> <tag>",
6208
6351
  " audienti companies search --query <text>",
@@ -6679,7 +6822,7 @@ const HELP_TOPICS = new Map([
6679
6822
  " audienti icps list [--tag <tag>] [--json]",
6680
6823
  " audienti icps show <icp_id> [--json]",
6681
6824
  " audienti icps create (--name <text> | --payload <file.json>) [--json]",
6682
- " audienti icps update <icp_id> [--tags <tag[,tag...]>] [--json]",
6825
+ " audienti icps update <icp_id> ([--tags <tag[,tag...]>] | --payload <file.json>) [--json]",
6683
6826
  " audienti icps add-tag <icp_id> <tag> [--json]",
6684
6827
  " audienti icps remove-tag <icp_id> <tag> [--json]",
6685
6828
  "",
@@ -6772,12 +6915,12 @@ const HELP_TOPICS = new Map([
6772
6915
 
6773
6916
  ["icps update", [
6774
6917
  "Usage:",
6775
- " audienti icps update <icp_id> [--name <text>] [--notes <text>] [--discovery-keyword <text>] [--tags <tag[,tag...]>] [--json] [--account <acct_id>]",
6918
+ ` ${ICPS_UPDATE_USAGE.slice("Usage: ".length)}`,
6776
6919
  "",
6777
6920
  "Status: implemented",
6778
6921
  "",
6779
6922
  "Purpose:",
6780
- " Update simple ICP fields or replace the ICP's full tag set.",
6923
+ " Update simple ICP fields, replace the ICP's full tag set, or apply a rich ICP payload patch.",
6781
6924
  "",
6782
6925
  "Input shape:",
6783
6926
  " icp_id: icpp_ prefixed id or integer id",
@@ -6785,15 +6928,30 @@ const HELP_TOPICS = new Map([
6785
6928
  " notes: string | optional",
6786
6929
  " discovery_keyword: string | optional",
6787
6930
  " tags: comma-separated tag list | optional",
6931
+ " payload: file.json | optional full or partial ICP object using the account API create shape",
6932
+ "",
6933
+ "Behavior:",
6934
+ " Choose either --payload or simple flags. Supplied facet collections replace that collection in place; omitted fields and facet collections remain unchanged.",
6935
+ " Human-readable lookup-backed facet names are resolved by the account API. Invalid lookup values fail with 422 and no partial mutation.",
6788
6936
  "",
6789
6937
  "API:",
6790
6938
  " PATCH /api/v1/accounts/:account_id/icps/:id.json",
6791
6939
  "",
6792
- "JSON body:",
6940
+ "Simple JSON body:",
6793
6941
  " {",
6794
6942
  " \"icp\": {",
6795
6943
  " \"tags\": [\"enterprise\", \"renewal\"]",
6796
6944
  " }",
6945
+ " }",
6946
+ "",
6947
+ "Payload file example:",
6948
+ " {",
6949
+ " \"company_sizes_attributes\": [",
6950
+ " {\"name\": \"1001-5000 employees\"}",
6951
+ " ],",
6952
+ " \"seniorities_attributes\": [",
6953
+ " {\"name\": \"CXO\"}",
6954
+ " ]",
6797
6955
  " }"
6798
6956
  ].join("\n")],
6799
6957
 
@@ -7393,11 +7551,12 @@ const HELP_TOPICS = new Map([
7393
7551
  " audienti motions show <motn_id> [--json]",
7394
7552
  " audienti motions status <motn_id> [--json]",
7395
7553
  " audienti motions run-discovery <motn_id> [--target-count <n>] [--json]",
7554
+ " audienti motions quick-start --url <company_url> [--confirm] [--wait] [--json]",
7396
7555
  " audienti motions analytics <motn_id> [--window 30d] [--json]",
7397
7556
  " audienti motions prospects <motn_id> [--json]",
7398
7557
  " audienti motions add-prospects <motn_id> <prsp_id> [prsp_id...] [--json]",
7399
7558
  " audienti motions create --payload <file.json> [--json]",
7400
- " audienti motions update <motn_id> [--status <draft|preparing|active|paused|archived>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>] [--json]",
7559
+ " audienti motions update <motn_id> ([--status <draft|preparing|active|paused|archived>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>] | --payload <file.json>) [--json]",
7401
7560
  " audienti motions add-tag <motn_id> <tag> [--json]",
7402
7561
  " audienti motions remove-tag <motn_id> <tag> [--json]",
7403
7562
  " audienti motions activate <motn_id> [--json]",
@@ -7407,7 +7566,7 @@ const HELP_TOPICS = new Map([
7407
7566
  " audienti motions clone <motn_id> --name <text> [--json]",
7408
7567
  " audienti motions move-prospects <source_motn_id> --target <target_motn_id> <prsp_id> [prsp_id...] [--json]",
7409
7568
  "",
7410
- "Status: read, create, status update, delete, clone, status, discovery launch, and prospect attachment commands implemented",
7569
+ "Status: read, create, quick-start, status update, delete, clone, status, discovery launch, and prospect attachment commands implemented",
7411
7570
  "",
7412
7571
  "CLI synonym:",
7413
7572
  " `plays` is accepted anywhere `motions` is accepted",
@@ -7495,6 +7654,7 @@ const HELP_TOPICS = new Map([
7495
7654
  " state: healthy_idle | broken",
7496
7655
  " reason_key: string",
7497
7656
  " reason_label: string",
7657
+ " executable_configuration: { valid: boolean, reason_key: string | null, reason_label: string | null, description: string | null }",
7498
7658
  " description: string",
7499
7659
  " action: { key: string, label: string } | null",
7500
7660
  " stats: { target_count, deficit, projected_connectable, capacity, daily_target }",
@@ -7545,6 +7705,36 @@ const HELP_TOPICS = new Map([
7545
7705
  " POST /api/v1/accounts/:account_id/motions/:id/run_discovery.json"
7546
7706
  ].join("\n")],
7547
7707
 
7708
+ ["motions quick-start", [
7709
+ "Usage:",
7710
+ ` ${MOTIONS_QUICK_START_USAGE.slice("Usage: ".length)}`,
7711
+ "",
7712
+ "Status: implemented",
7713
+ "",
7714
+ "Purpose:",
7715
+ " Draft a quick-start motion from a company URL, then optionally confirm it into a launched quick-start motion.",
7716
+ "",
7717
+ "Options:",
7718
+ " --url <company_url> Public HTTP(S) URL used by the quick-start drafter.",
7719
+ " --principal <user> Account user id or me. Defaults to the authenticated token user.",
7720
+ " --feedback <text> Optional operator guidance for the draft.",
7721
+ " --offer-type <type> Optional supported offer type hint.",
7722
+ " --force Request a fresh draft when inputs should replace an existing ready draft.",
7723
+ " --confirm Confirm the ready draft into motion setup and discovery launch.",
7724
+ " --wait Poll until the generated draft is ready before returning or confirming.",
7725
+ " --timeout-seconds <n> Maximum wait time. Defaults to 60.",
7726
+ " --poll-interval-seconds <n> Poll cadence. Defaults to 2.",
7727
+ "",
7728
+ "Pipeline:",
7729
+ " POST creates or reuses a QuickStartDraft and enqueues GenerateQuickStartDraftJob when needed.",
7730
+ " POST confirm calls QuickStartOrchestrator, creating ICP, offer, signals, motion, reservation, and discovery launch.",
7731
+ "",
7732
+ "API:",
7733
+ " POST /api/v1/accounts/:account_id/quick_start.json",
7734
+ " GET /api/v1/accounts/:account_id/quick_start/:draft_id.json",
7735
+ " POST /api/v1/accounts/:account_id/quick_start/:draft_id/confirm.json"
7736
+ ].join("\n")],
7737
+
7548
7738
  ["motions prospects", [
7549
7739
  "Usage:",
7550
7740
  " audienti motions prospects <motn_id> [--json] [--account <acct_id>]",
@@ -7616,7 +7806,9 @@ const HELP_TOPICS = new Map([
7616
7806
  " icp_id: icpp_ prefix id | optional",
7617
7807
  " list_id: list_ prefix id | optional",
7618
7808
  " play_tags: [string] | optional",
7619
- " inbound_channels: [linkedin | reddit] | optional",
7809
+ " signal_rows: [{ scope: company | person | both, question: string, company_signal_category: string | optional, role_terms: string | optional, posting_language: string | optional, topics: string | optional }] | optional for outbound motions",
7810
+ " signal_questions: newline-delimited company::, person::, or both:: legacy question text | optional for outbound motions",
7811
+ " inbound_channels: [linkedin | reddit] | optional; defaults to [linkedin] for inbound motions",
7620
7812
  " lopa_profiles: [{ url: string, source_type: creator | competitor | partner | customer | other }] | optional",
7621
7813
  "",
7622
7814
  "JSON example:",
@@ -7671,25 +7863,44 @@ const HELP_TOPICS = new Map([
7671
7863
  "Status: implemented",
7672
7864
  "",
7673
7865
  "Purpose:",
7674
- " Change one motion or play's lifecycle status or replace its tag set.",
7866
+ " Change one motion or play's lifecycle status, replace its tag set, or patch rich motion configuration.",
7675
7867
  "",
7676
7868
  "Input shape:",
7677
7869
  " motn_id: motn_ prefix id",
7678
7870
  " status: draft | preparing | active | paused | archived | optional",
7679
7871
  " tags: comma-separated tag list | optional",
7872
+ " payload: file.json | optional full or partial motion object using the account API shape",
7680
7873
  "",
7681
7874
  "Behavior:",
7682
- " Updates only the provided fields. Sending --tags replaces the motion's full tag set.",
7875
+ " Choose either --payload or simple flags. Updates only the provided fields. Sending --tags replaces the motion's full tag set.",
7876
+ " Payload mode supports outbound signal_rows or legacy signal_questions; supplied rows replace the motion's active ready signals.",
7683
7877
  "",
7684
7878
  "API:",
7685
7879
  " PATCH /api/v1/accounts/:account_id/motions/:id.json",
7686
7880
  "",
7687
- "JSON body:",
7881
+ "Simple JSON body:",
7688
7882
  " {",
7689
7883
  " \"motion\": {",
7690
7884
  " \"status\": \"paused\",",
7691
7885
  " \"play_tags\": [\"sarit\", \"pj\"]",
7692
7886
  " }",
7887
+ " }",
7888
+ "",
7889
+ "Payload file example:",
7890
+ " {",
7891
+ " \"signal_rows\": [",
7892
+ " {",
7893
+ " \"scope\": \"company\",",
7894
+ " \"question\": \"Is the company hiring revenue operations leaders?\",",
7895
+ " \"company_signal_category\": \"hiring\",",
7896
+ " \"role_terms\": \"Revenue Operations\"",
7897
+ " },",
7898
+ " {",
7899
+ " \"scope\": \"person\",",
7900
+ " \"question\": \"Is the person talking about pipeline quality?\",",
7901
+ " \"topics\": \"pipeline quality\"",
7902
+ " }",
7903
+ " ]",
7693
7904
  " }"
7694
7905
  ].join("\n")],
7695
7906