@audienti/cli 0.1.17 → 0.1.19

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,23 @@ All notable changes to the Audienti CLI are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.1.19] - 2026-07-17
8
+
9
+ ### Added
10
+
11
+ - Add `audienti tools linkedin-review --url <linkedin_url> [--icp <icp_id>]` to queue the LinkedIn Review / Blueprint report from the CLI.
12
+ - Add `audienti tools list` to show available CLI-backed tools and their report inspection commands.
13
+ - Add `audienti tools linkedin-review reports [--limit <n>]` to list recent LinkedIn Review reports and find report ids.
14
+ - Add `audienti tools linkedin-review show <rprt_id>` to print completed report content in the terminal.
15
+ - Add `audienti tools linkedin-review status <rprt_id>` to inspect report stage, run status, timestamps, and product URL while the profile review is building.
16
+
17
+ ## [0.1.18] - 2026-07-17
18
+
19
+ ### Added
20
+
21
+ - Add ContentOps CLI commands for inspecting programs, reviewing plan rows, approving/scheduling/publishing work items, and handling comment tasks.
22
+ - Add `audienti motions update <motn_id> --own-post-engagement <true|false>` to enable or disable owned-post engagement intake for inbound motions.
23
+
7
24
  ## [0.1.17] - 2026-07-17
8
25
 
9
26
  ### Added
package/README.md CHANGED
@@ -63,8 +63,13 @@ audienti writer test-run <prsp_id>
63
63
  audienti motions analytics <motn_id>
64
64
  audienti motions run-discovery <motn_id>
65
65
  audienti motions update <motn_id> --status paused
66
+ audienti motions update <motn_id> --own-post-engagement true
66
67
  audienti motions activate <motn_id>
67
68
  audienti motions delete <motn_id> --confirm yes
69
+ audienti content programs
70
+ audienti content plan <cprg_id>
71
+ audienti content approve <cpwi_id>
72
+ audienti content comments
68
73
  audienti prospects show <prsp_id> --json
69
74
  audienti prospects list --profiles
70
75
  audienti prospects list --assigned-user unassigned
@@ -80,6 +85,11 @@ audienti analytics dashboard --play-tag wine_campaign
80
85
  audienti analytics users --user me --window 30d
81
86
  audienti analytics visibility --window 24h --user me
82
87
  audienti analytics content --window week
88
+ audienti tools list
89
+ audienti tools linkedin-review --url https://www.linkedin.com/in/example --icp <icp_id>
90
+ audienti tools linkedin-review reports
91
+ audienti tools linkedin-review show <rprt_id>
92
+ audienti tools linkedin-review status <rprt_id>
83
93
  ```
84
94
 
85
95
  To let an agent or operator check whether the local CLI is behind the latest
@@ -167,6 +177,18 @@ audienti prospects add-profile <prsp_id> --url https://www.linkedin.com/in/examp
167
177
  audienti prospects report-bad-profile <prsp_id> <prof_id>
168
178
  ```
169
179
 
180
+ To queue a personal LinkedIn profile authority review and ICP-fit positioning
181
+ blueprint, then check whether it is waiting on enrichment, running, completed,
182
+ or failed:
183
+
184
+ ```bash
185
+ audienti tools list
186
+ audienti tools linkedin-review --url https://www.linkedin.com/in/example --icp <icp_id>
187
+ audienti tools linkedin-review reports
188
+ audienti tools linkedin-review show <rprt_id>
189
+ audienti tools linkedin-review status <rprt_id>
190
+ ```
191
+
170
192
  To reassign or clear ownership for existing prospects:
171
193
 
172
194
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audienti/cli",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "Agent-first command-line client for Audienti.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -67,4 +67,9 @@ audienti operator next --plan
67
67
  audienti analytics prospects --window 24h --json
68
68
  audienti analytics visibility --window 24h --user me --json
69
69
  audienti analytics content --window week --json
70
+ audienti tools list --json
71
+ audienti tools linkedin-review --url https://www.linkedin.com/in/example --icp <icp_id> --json
72
+ audienti tools linkedin-review reports --json
73
+ audienti tools linkedin-review show <rprt_id> --json
74
+ audienti tools linkedin-review status <rprt_id> --json
70
75
  ```
package/src/api-client.js CHANGED
@@ -261,6 +261,46 @@ export class AudientiClient {
261
261
  });
262
262
  }
263
263
 
264
+ contentPrograms(accountId, query = {}) {
265
+ return this.requestJson(accountPath(accountId, ["content_ops", "programs"], query));
266
+ }
267
+
268
+ contentPlan(accountId, programId, query = {}) {
269
+ return this.requestJson(accountPath(accountId, ["content_ops", "programs", programId, "plan"], query));
270
+ }
271
+
272
+ contentWorkItem(accountId, workItemId) {
273
+ return this.requestJson(accountPath(accountId, ["content_ops", "work_items", workItemId]));
274
+ }
275
+
276
+ contentFeedback(accountId, workItemId, body) {
277
+ return this.requestJson(accountPath(accountId, ["content_ops", "work_items", workItemId, "feedback"]), { method: "POST", body });
278
+ }
279
+
280
+ contentApprove(accountId, workItemId) {
281
+ return this.requestJson(accountPath(accountId, ["content_ops", "work_items", workItemId, "approve"]), { method: "POST" });
282
+ }
283
+
284
+ contentSchedule(accountId, workItemId, body) {
285
+ return this.requestJson(accountPath(accountId, ["content_ops", "work_items", workItemId, "schedule"]), { method: "POST", body });
286
+ }
287
+
288
+ contentPublish(accountId, workItemId, body) {
289
+ return this.requestJson(accountPath(accountId, ["content_ops", "work_items", workItemId, "publish"]), { method: "POST", body });
290
+ }
291
+
292
+ contentComments(accountId, query = {}) {
293
+ return this.requestJson(accountPath(accountId, ["content_ops", "comment_tasks"], query));
294
+ }
295
+
296
+ contentReply(accountId, commentTaskId, body) {
297
+ return this.requestJson(accountPath(accountId, ["content_ops", "comment_tasks", commentTaskId, "send_reply"]), { method: "POST", body });
298
+ }
299
+
300
+ contentDismiss(accountId, commentTaskId) {
301
+ return this.requestJson(accountPath(accountId, ["content_ops", "comment_tasks", commentTaskId, "dismiss"]), { method: "POST" });
302
+ }
303
+
264
304
  addMotionTag(accountId, motionId, body) {
265
305
  return this.requestJson(accountPath(accountId, ["motions", motionId, "add_tag"]), {
266
306
  method: "POST",
@@ -425,6 +465,21 @@ export class AudientiClient {
425
465
  return this.requestJson(accountPath(accountId, ["prospect_imports", importId]));
426
466
  }
427
467
 
468
+ linkedinReviewReports(accountId, query = {}) {
469
+ return this.requestJson(accountPath(accountId, ["tools", "linkedin-review", "reports"], query));
470
+ }
471
+
472
+ linkedinReview(accountId, body) {
473
+ return this.requestJson(accountPath(accountId, ["tools", "linkedin-review", "reports"]), {
474
+ method: "POST",
475
+ body
476
+ });
477
+ }
478
+
479
+ linkedinReviewStatus(accountId, reportId) {
480
+ return this.requestJson(accountPath(accountId, ["tools", "linkedin-review", "reports", reportId]));
481
+ }
482
+
428
483
  operatorQueue(accountId, query = {}) {
429
484
  return this.requestJson(accountPath(accountId, ["operator"], query));
430
485
  }
package/src/cli.js CHANGED
@@ -55,7 +55,17 @@ const OFFERS_UPDATE_USAGE = "Usage: audienti offers update <offr_id> [--name <te
55
55
  const OFFERS_DELETE_USAGE = "Usage: audienti offers delete <offr_id> --confirm <yes|true|Y|y> [--json] [--account <acct_id>]";
56
56
  const WRITER_TEST_RUN_USAGE = "Usage: audienti writer test-run <prsp_id> [--json] [--mode <report|plan|step>] [--branch <both|no-accept|accepted>] [--step <step_key|row_number>] [--no-cache] [--clear-cache] [--account <acct_id>]";
57
57
  const MOTIONS_ANALYTICS_USAGE = "Usage: audienti motions analytics <motn_id> [--window 30d] [--json] [--account <acct_id>]";
58
- const MOTIONS_UPDATE_USAGE = "Usage: audienti motions update <motn_id> [--status <draft|preparing|active|paused|archived>] [--tags <tag[,tag...]>] [--json] [--account <acct_id>]";
58
+ 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>]";
59
+ const CONTENT_PROGRAMS_USAGE = "Usage: audienti content programs [--user <account_user_id|email|name|me>] [--json] [--account <acct_id>]";
60
+ const CONTENT_PLAN_USAGE = "Usage: audienti content plan <cprg_id> [--week <n>] [--due] [--json] [--account <acct_id>]";
61
+ const CONTENT_SHOW_USAGE = "Usage: audienti content show <cpwi_id> [--json] [--account <acct_id>]";
62
+ const CONTENT_FEEDBACK_USAGE = "Usage: audienti content feedback <cpwi_id> (--message <text> | --payload <file.json>) [--json] [--account <acct_id>]";
63
+ const CONTENT_APPROVE_USAGE = "Usage: audienti content approve <cpwi_id> [--json] [--account <acct_id>]";
64
+ const CONTENT_SCHEDULE_USAGE = "Usage: audienti content schedule <cpwi_id> --at <time> [--json] [--account <acct_id>]";
65
+ const CONTENT_PUBLISH_USAGE = "Usage: audienti content publish <cpwi_id> --url <permalink> [--json] [--account <acct_id>]";
66
+ const CONTENT_COMMENTS_USAGE = "Usage: audienti content comments [--unresolved] [--user <account_user_id|email|name|me>] [--json] [--account <acct_id>]";
67
+ const CONTENT_REPLY_USAGE = "Usage: audienti content reply <cctk_id> [--body <text>] [--json] [--account <acct_id>]";
68
+ const CONTENT_DISMISS_USAGE = "Usage: audienti content dismiss <cctk_id> [--json] [--account <acct_id>]";
59
69
  const MOTIONS_ADD_TAG_USAGE = "Usage: audienti motions add-tag <motn_id> <tag> [--json] [--account <acct_id>]";
60
70
  const MOTIONS_REMOVE_TAG_USAGE = "Usage: audienti motions remove-tag <motn_id> <tag> [--json] [--account <acct_id>]";
61
71
  const MOTIONS_DELETE_USAGE = "Usage: audienti motions delete <motn_id> --confirm <yes|true|Y|y> [--json] [--account <acct_id>]";
@@ -72,6 +82,11 @@ const ANALYTICS_PROSPECTS_USAGE = "Usage: audienti analytics prospects [--window
72
82
  const ANALYTICS_PROSPECTS_COHORT_ANALYSIS_USAGE = "Usage: audienti analytics prospects cohort-analysis [--weeks <n>] [--window 24h] [--motion <motn_id>] [--provenance <source>] [--user <account_user_id|email|name|me>] [--json] [--account <acct_id>]";
73
83
  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>] [--provenance <source>] [--platform <linkedin|email|gmail>] [--json] [--account <acct_id>]";
74
84
  const ANALYTICS_DASHBOARD_USAGE = "Usage: audienti analytics dashboard [--cohort-start YYYY-MM-DD --cohort-end YYYY-MM-DD] [--play-tag <tag>] [--motion <motn_id>] [--offer <offr_id>] [--icp <icp_id>] [--user <account_user_id|email|name|me>] [--json] [--account <acct_id>]";
85
+ const TOOLS_LIST_USAGE = "Usage: audienti tools list [--json]";
86
+ const TOOLS_LINKEDIN_REVIEW_USAGE = "Usage: audienti tools linkedin-review --url <linkedin_url> [--icp <icp_id>] [--json] [--account <acct_id>]";
87
+ const TOOLS_LINKEDIN_REVIEW_REPORTS_USAGE = "Usage: audienti tools linkedin-review reports [--limit <n>] [--json] [--account <acct_id>]";
88
+ const TOOLS_LINKEDIN_REVIEW_SHOW_USAGE = "Usage: audienti tools linkedin-review show <rprt_id> [--json] [--account <acct_id>]";
89
+ const TOOLS_LINKEDIN_REVIEW_STATUS_USAGE = "Usage: audienti tools linkedin-review status <rprt_id> [--json] [--account <acct_id>]";
75
90
  const COHORT_STAGE_ORDER = [
76
91
  "identified",
77
92
  "pre_connect",
@@ -204,6 +219,16 @@ async function dispatch(argv, context) {
204
219
  if (normalizedResource === "motions" && action === "clone") return motionsClone(rest, context, { accountOverride });
205
220
  if (normalizedResource === "motions" && action === "move-prospects") return motionsMoveProspects(rest, context, { accountOverride });
206
221
  if (normalizedResource === "motions" && action === "run-discovery") return motionsRunDiscovery(rest, context, { accountOverride });
222
+ if (normalizedResource === "content" && action === "programs") return contentPrograms(rest, context, { accountOverride });
223
+ if (normalizedResource === "content" && action === "plan") return contentPlan(rest, context, { accountOverride });
224
+ if (normalizedResource === "content" && action === "show") return contentShow(rest, context, { accountOverride });
225
+ if (normalizedResource === "content" && action === "feedback") return contentFeedback(rest, context, { accountOverride });
226
+ if (normalizedResource === "content" && action === "approve") return contentApprove(rest, context, { accountOverride });
227
+ if (normalizedResource === "content" && action === "schedule") return contentSchedule(rest, context, { accountOverride });
228
+ if (normalizedResource === "content" && action === "publish") return contentPublish(rest, context, { accountOverride });
229
+ if (normalizedResource === "content" && action === "comments") return contentComments(rest, context, { accountOverride });
230
+ if (normalizedResource === "content" && action === "reply") return contentReply(rest, context, { accountOverride });
231
+ if (normalizedResource === "content" && action === "dismiss") return contentDismiss(rest, context, { accountOverride });
207
232
  if (normalizedResource === "prospects" && action === "list") return prospectsList(rest, context, { accountOverride });
208
233
  if (normalizedResource === "prospects" && action === "check") return prospectsCheck(rest, context, { accountOverride });
209
234
  if (normalizedResource === "prospects" && action === "show") return prospectsShow(rest, context, { accountOverride });
@@ -227,7 +252,9 @@ async function dispatch(argv, context) {
227
252
  if (normalizedResource === "prospects" && action === "import-batch") return prospectsImportBatch(rest, context, { accountOverride });
228
253
  if (normalizedResource === "prospects" && action === "import-status") return prospectsImportStatus(rest, context, { accountOverride });
229
254
  if (normalizedResource === "writer" && action === "test-run") return writerTestRun(rest, context, { accountOverride });
255
+ if (normalizedResource === "tools" && action === "list") return toolsList(rest, context);
230
256
  if (normalizedResource === "tools" && action === "get") return toolsGet(rest, context, { accountOverride });
257
+ if (normalizedResource === "tools" && action === "linkedin-review") return toolsLinkedinReview(rest, context, { accountOverride });
231
258
  if (normalizedResource === "operator" && action === "queue") return operatorQueue(rest, context, { accountOverride });
232
259
  if (normalizedResource === "operator" && action === "next") return operatorNext(rest, context, { accountOverride });
233
260
  if (normalizedResource === "operator" && action === "outcome") return operatorOutcome(rest, context, { accountOverride });
@@ -1360,9 +1387,10 @@ async function motionsUpdate(args, context, { accountOverride } = {}) {
1360
1387
  const { values, positionals } = parseCommandArgs(args, {
1361
1388
  ...jsonOptions(),
1362
1389
  status: { type: "string" },
1363
- tags: { type: "string" }
1390
+ tags: { type: "string" },
1391
+ "own-post-engagement": { type: "string" }
1364
1392
  });
1365
- const hasUpdateField = values.status || values.tags !== undefined;
1393
+ const hasUpdateField = values.status || values.tags !== undefined || values["own-post-engagement"] !== undefined;
1366
1394
  if (positionals.length !== 1 || !hasUpdateField) {
1367
1395
  throw new CommandError(MOTIONS_UPDATE_USAGE);
1368
1396
  }
@@ -1370,7 +1398,8 @@ async function motionsUpdate(args, context, { accountOverride } = {}) {
1370
1398
  const normalizedStatus = normalizeMotionStatus(values.status);
1371
1399
  const motionAttributes = compactObject({
1372
1400
  status: normalizedStatus,
1373
- play_tags: values.tags !== undefined ? tagList(values.tags) : undefined
1401
+ play_tags: values.tags !== undefined ? tagList(values.tags) : undefined,
1402
+ own_post_engagement: values["own-post-engagement"] !== undefined ? parseBooleanString(values["own-post-engagement"], "--own-post-engagement") : undefined
1374
1403
  });
1375
1404
 
1376
1405
  const { client, accountId } = await requireAccountContext(context, { accountOverride });
@@ -1411,6 +1440,149 @@ async function updateMotionStatus(motionId, status, context, { accountOverride,
1411
1440
  renderMotion(motion, context);
1412
1441
  }
1413
1442
 
1443
+ async function contentPrograms(args, context, { accountOverride } = {}) {
1444
+ const { values, positionals } = parseCommandArgs(args, {
1445
+ ...jsonOptions(),
1446
+ user: { type: "string" }
1447
+ });
1448
+ if (positionals.length > 0) throw new CommandError(CONTENT_PROGRAMS_USAGE);
1449
+
1450
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
1451
+ const programs = await client.contentPrograms(accountId, compactObject({ user: values.user }));
1452
+ if (values.json) return writeJson(context.stdout, programs);
1453
+
1454
+ renderContentPrograms(programs, context);
1455
+ }
1456
+
1457
+ async function contentPlan(args, context, { accountOverride } = {}) {
1458
+ const { values, positionals } = parseCommandArgs(args, {
1459
+ ...jsonOptions(),
1460
+ week: { type: "string" },
1461
+ due: { type: "boolean" }
1462
+ });
1463
+ if (positionals.length !== 1) throw new CommandError(CONTENT_PLAN_USAGE);
1464
+
1465
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
1466
+ const payload = await client.contentPlan(accountId, positionals[0]);
1467
+ if (values.json) return writeJson(context.stdout, payload);
1468
+
1469
+ const week = values.week ? Number.parseInt(values.week, 10) : null;
1470
+ let rows = Array.isArray(payload?.rows) ? payload.rows : [];
1471
+ if (week) rows = rows.filter((row) => Number(row.week_number) === week);
1472
+ if (values.due) rows = rows.filter((row) => ["researching", "drafting", "needs_operator_review", "needs_operator_approval", "scheduled", "ready_to_post"].includes(row.stage));
1473
+ renderContentPlanRows(rows, context);
1474
+ }
1475
+
1476
+ async function contentShow(args, context, { accountOverride } = {}) {
1477
+ const { values, positionals } = parseCommandArgs(args, jsonOptions());
1478
+ if (positionals.length !== 1) throw new CommandError(CONTENT_SHOW_USAGE);
1479
+
1480
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
1481
+ const item = await client.contentWorkItem(accountId, positionals[0]);
1482
+ if (values.json) return writeJson(context.stdout, item);
1483
+
1484
+ renderContentWorkItem(item, context);
1485
+ }
1486
+
1487
+ async function contentFeedback(args, context, { accountOverride } = {}) {
1488
+ const { values, positionals } = parseCommandArgs(args, {
1489
+ ...jsonOptions(),
1490
+ message: { type: "string" },
1491
+ payload: { type: "string" }
1492
+ });
1493
+ if (positionals.length !== 1 || (!values.message && !values.payload)) throw new CommandError(CONTENT_FEEDBACK_USAGE);
1494
+
1495
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
1496
+ const payload = values.payload ? await readJsonPayload(values.payload) : { draft_feedback: values.message };
1497
+ const item = await client.contentFeedback(accountId, positionals[0], payload);
1498
+ if (values.json) return writeJson(context.stdout, item);
1499
+
1500
+ writeLine(context.stdout, `Queued feedback for ${display(item?.prefix_id)}.`);
1501
+ renderContentWorkItem(item, context);
1502
+ }
1503
+
1504
+ async function contentApprove(args, context, { accountOverride } = {}) {
1505
+ const { values, positionals } = parseCommandArgs(args, jsonOptions());
1506
+ if (positionals.length !== 1) throw new CommandError(CONTENT_APPROVE_USAGE);
1507
+
1508
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
1509
+ const item = await client.contentApprove(accountId, positionals[0]);
1510
+ if (values.json) return writeJson(context.stdout, item);
1511
+
1512
+ writeLine(context.stdout, `Approved ${display(item?.prefix_id)}.`);
1513
+ renderContentWorkItem(item, context);
1514
+ }
1515
+
1516
+ async function contentSchedule(args, context, { accountOverride } = {}) {
1517
+ const { values, positionals } = parseCommandArgs(args, {
1518
+ ...jsonOptions(),
1519
+ at: { type: "string" }
1520
+ });
1521
+ if (positionals.length !== 1 || !values.at) throw new CommandError(CONTENT_SCHEDULE_USAGE);
1522
+
1523
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
1524
+ const item = await client.contentSchedule(accountId, positionals[0], { scheduled_at: values.at });
1525
+ if (values.json) return writeJson(context.stdout, item);
1526
+
1527
+ writeLine(context.stdout, `Scheduled ${display(item?.prefix_id)}.`);
1528
+ renderContentWorkItem(item, context);
1529
+ }
1530
+
1531
+ async function contentPublish(args, context, { accountOverride } = {}) {
1532
+ const { values, positionals } = parseCommandArgs(args, {
1533
+ ...jsonOptions(),
1534
+ url: { type: "string" }
1535
+ });
1536
+ if (positionals.length !== 1 || !values.url) throw new CommandError(CONTENT_PUBLISH_USAGE);
1537
+
1538
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
1539
+ const item = await client.contentPublish(accountId, positionals[0], { permalink: values.url });
1540
+ if (values.json) return writeJson(context.stdout, item);
1541
+
1542
+ writeLine(context.stdout, `Published ${display(item?.prefix_id)}.`);
1543
+ renderContentWorkItem(item, context);
1544
+ }
1545
+
1546
+ async function contentComments(args, context, { accountOverride } = {}) {
1547
+ const { values, positionals } = parseCommandArgs(args, {
1548
+ ...jsonOptions(),
1549
+ unresolved: { type: "boolean" },
1550
+ user: { type: "string" }
1551
+ });
1552
+ if (positionals.length > 0) throw new CommandError(CONTENT_COMMENTS_USAGE);
1553
+
1554
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
1555
+ const comments = await client.contentComments(accountId, compactObject({ unresolved: values.unresolved === false ? false : true, user: values.user }));
1556
+ if (values.json) return writeJson(context.stdout, comments);
1557
+
1558
+ renderContentComments(comments, context);
1559
+ }
1560
+
1561
+ async function contentReply(args, context, { accountOverride } = {}) {
1562
+ const { values, positionals } = parseCommandArgs(args, {
1563
+ ...jsonOptions(),
1564
+ body: { type: "string" }
1565
+ });
1566
+ if (positionals.length !== 1) throw new CommandError(CONTENT_REPLY_USAGE);
1567
+
1568
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
1569
+ const comment = await client.contentReply(accountId, positionals[0], compactObject({ body: values.body }));
1570
+ if (values.json) return writeJson(context.stdout, comment);
1571
+
1572
+ writeLine(context.stdout, `Sent reply for ${display(comment?.prefix_id)}.`);
1573
+ }
1574
+
1575
+ async function contentDismiss(args, context, { accountOverride } = {}) {
1576
+ const { values, positionals } = parseCommandArgs(args, jsonOptions());
1577
+ if (positionals.length !== 1) throw new CommandError(CONTENT_DISMISS_USAGE);
1578
+
1579
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
1580
+ const comment = await client.contentDismiss(accountId, positionals[0]);
1581
+ if (values.json) return writeJson(context.stdout, comment);
1582
+
1583
+ writeLine(context.stdout, `Dismissed ${display(comment?.prefix_id)}.`);
1584
+ }
1585
+
1414
1586
  function normalizeMotionStatus(status) {
1415
1587
  if (status === undefined) return undefined;
1416
1588
 
@@ -1422,6 +1594,14 @@ function normalizeMotionStatus(status) {
1422
1594
  return normalizedStatus;
1423
1595
  }
1424
1596
 
1597
+ function parseBooleanString(value, flagName) {
1598
+ const normalized = String(value || "").trim().toLowerCase();
1599
+ if (["true", "1", "yes", "y"].includes(normalized)) return true;
1600
+ if (["false", "0", "no", "n"].includes(normalized)) return false;
1601
+
1602
+ throw new CommandError(`${flagName} must be true or false.`);
1603
+ }
1604
+
1425
1605
  async function motionsTagMutation(action, args, context, { accountOverride } = {}) {
1426
1606
  const usageText = action === "add" ? MOTIONS_ADD_TAG_USAGE : MOTIONS_REMOVE_TAG_USAGE;
1427
1607
  const { values, positionals } = parseCommandArgs(args, jsonOptions());
@@ -2217,6 +2397,88 @@ async function toolsGet(args, context, { accountOverride } = {}) {
2217
2397
  if (response.import_id) writeLine(context.stdout, `Import: ${response.import_id}`);
2218
2398
  }
2219
2399
 
2400
+ async function toolsLinkedinReview(args, context, { accountOverride } = {}) {
2401
+ if (args[0] === "reports") return toolsLinkedinReviewReports(args.slice(1), context, { accountOverride });
2402
+ if (args[0] === "show") return toolsLinkedinReviewShow(args.slice(1), context, { accountOverride });
2403
+ if (args[0] === "status") return toolsLinkedinReviewStatus(args.slice(1), context, { accountOverride });
2404
+
2405
+ const { values, positionals } = parseCommandArgs(args, {
2406
+ ...jsonOptions(),
2407
+ url: { type: "string" },
2408
+ icp: { type: "string" }
2409
+ });
2410
+ const linkedinUrl = (values.url || positionals[0] || "").trim();
2411
+
2412
+ if (!linkedinUrl || positionals.length > (values.url ? 0 : 1)) {
2413
+ throw new CommandError(TOOLS_LINKEDIN_REVIEW_USAGE);
2414
+ }
2415
+
2416
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
2417
+ const payload = await client.linkedinReview(accountId, {
2418
+ linkedin_url: linkedinUrl,
2419
+ icp_id: values.icp
2420
+ });
2421
+ if (values.json) return writeJson(context.stdout, payload);
2422
+
2423
+ renderLinkedinReviewStarted(payload, context);
2424
+ }
2425
+
2426
+ async function toolsList(args, context) {
2427
+ const { values, positionals } = parseCommandArgs(args, jsonOptions());
2428
+ if (positionals.length > 0) throw new CommandError(TOOLS_LIST_USAGE);
2429
+
2430
+ const payload = { tools: availableTools() };
2431
+ if (values.json) return writeJson(context.stdout, payload);
2432
+
2433
+ renderToolsList(payload.tools, context);
2434
+ }
2435
+
2436
+ async function toolsLinkedinReviewReports(args, context, { accountOverride } = {}) {
2437
+ const { values, positionals } = parseCommandArgs(args, {
2438
+ ...jsonOptions(),
2439
+ limit: { type: "string" }
2440
+ });
2441
+
2442
+ if (positionals.length > 0) {
2443
+ throw new CommandError(TOOLS_LINKEDIN_REVIEW_REPORTS_USAGE);
2444
+ }
2445
+
2446
+ const limit = boundedListLimit(values.limit);
2447
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
2448
+ const payload = await client.linkedinReviewReports(accountId, { limit });
2449
+ if (values.json) return writeJson(context.stdout, payload);
2450
+
2451
+ renderLinkedinReviewReports(payload, context);
2452
+ }
2453
+
2454
+ async function toolsLinkedinReviewShow(args, context, { accountOverride } = {}) {
2455
+ const { values, positionals } = parseCommandArgs(args, jsonOptions());
2456
+
2457
+ if (positionals.length !== 1) {
2458
+ throw new CommandError(TOOLS_LINKEDIN_REVIEW_SHOW_USAGE);
2459
+ }
2460
+
2461
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
2462
+ const payload = await client.linkedinReviewStatus(accountId, positionals[0]);
2463
+ if (values.json) return writeJson(context.stdout, payload);
2464
+
2465
+ renderLinkedinReviewReport(payload, context);
2466
+ }
2467
+
2468
+ async function toolsLinkedinReviewStatus(args, context, { accountOverride } = {}) {
2469
+ const { values, positionals } = parseCommandArgs(args, jsonOptions());
2470
+
2471
+ if (positionals.length !== 1) {
2472
+ throw new CommandError(TOOLS_LINKEDIN_REVIEW_STATUS_USAGE);
2473
+ }
2474
+
2475
+ const { client, accountId } = await requireAccountContext(context, { accountOverride });
2476
+ const payload = await client.linkedinReviewStatus(accountId, positionals[0]);
2477
+ if (values.json) return writeJson(context.stdout, payload);
2478
+
2479
+ renderLinkedinReviewStatus(payload, context);
2480
+ }
2481
+
2220
2482
  async function operatorQueue(args, context, { accountOverride } = {}) {
2221
2483
  const { values, positionals } = parseCommandArgs(args, operatorFilterOptions());
2222
2484
  if (positionals.length > 0) throw new CommandError("Usage: audienti operator queue [--json] [filters] [--account <acct_id>]");
@@ -2455,6 +2717,15 @@ function jsonOptions() {
2455
2717
  };
2456
2718
  }
2457
2719
 
2720
+ function boundedListLimit(value) {
2721
+ if (value === undefined || value === null || value === "") return DEFAULT_LIST_LIMIT;
2722
+
2723
+ const parsed = Number.parseInt(String(value), 10);
2724
+ if (!Number.isFinite(parsed) || parsed < 1) throw new CommandError("--limit must be a positive integer.");
2725
+
2726
+ return Math.min(parsed, API_MAX_LIST_LIMIT);
2727
+ }
2728
+
2458
2729
  function operatorFilterOptions(extra = {}) {
2459
2730
  return {
2460
2731
  ...jsonOptions(),
@@ -3451,6 +3722,7 @@ function renderMotion(motion, context) {
3451
3722
  if (motion?.offer?.name) writeLine(context.stdout, `Offer: ${motion.offer.name} (${display(motion.offer.prefix_id)})`);
3452
3723
  if (motion?.icp?.name) writeLine(context.stdout, `ICP: ${motion.icp.name} (${display(motion.icp.prefix_id)})`);
3453
3724
  if (motion?.list?.name) writeLine(context.stdout, `List: ${motion.list.name} (${display(motion.list.prefix_id)})`);
3725
+ writeLine(context.stdout, `Own-post engagement: ${motion?.own_post_engagement ? "enabled" : "disabled"}`);
3454
3726
  if (Array.isArray(motion?.play_tags)) writeLine(context.stdout, `Tags: ${display(motion.play_tags.join(", "), "-")}`);
3455
3727
  if (motion?.principal_account_user?.id) {
3456
3728
  writeLine(
@@ -3460,6 +3732,59 @@ function renderMotion(motion, context) {
3460
3732
  }
3461
3733
  }
3462
3734
 
3735
+ function renderContentPrograms(programs, context) {
3736
+ if (!Array.isArray(programs) || programs.length === 0) return writeLine(context.stdout, "No ContentOps programs found.");
3737
+
3738
+ writeAlignedTable(context, ["PROGRAM ID", "OWNER", "STAGE", "BLOCKED", "CURRENT", "SCHEDULED", "STALE"], programs.map((program) => [
3739
+ display(program.prefix_id),
3740
+ display(program.owner?.name || program.owner?.email),
3741
+ display(program.stage),
3742
+ display(program.blocking_reason, "-"),
3743
+ display(program.current_piece?.prefix_id || program.current_piece?.piece_key, "-"),
3744
+ display(program.scheduled_piece?.prefix_id || program.scheduled_piece?.piece_key, "-"),
3745
+ program.plan_stale_motion_set ? "yes" : "no"
3746
+ ]));
3747
+ }
3748
+
3749
+ function renderContentPlanRows(rows, context) {
3750
+ if (!Array.isArray(rows) || rows.length === 0) return writeLine(context.stdout, "No ContentOps plan rows found.");
3751
+
3752
+ writeAlignedTable(context, ["DAY", "DATE", "MOTION", "STAGE", "STATUS", "TITLE"], rows.map((row) => [
3753
+ display(row.day_number),
3754
+ display(row.planned_publish_on, "-"),
3755
+ display(row.motion?.name || row.motion?.motion_prefix_id, "-"),
3756
+ display(row.stage, "-"),
3757
+ display(row.publish_status || row.workflow_phase, "-"),
3758
+ display(row.title)
3759
+ ]));
3760
+ }
3761
+
3762
+ function renderContentWorkItem(item, context) {
3763
+ writeLine(context.stdout, `Content item: ${display(item?.title)} (${display(item?.prefix_id)})`);
3764
+ writeLine(context.stdout, `Stage: ${display(item?.stage)}`);
3765
+ writeLine(context.stdout, `Blocking: ${display(item?.blocking_reason, "-")}`);
3766
+ if (item?.motion?.name || item?.motion?.motion_prefix_id) writeLine(context.stdout, `Motion: ${display(item.motion.name || item.motion.motion_prefix_id)}`);
3767
+ if (item?.scheduled_at) writeLine(context.stdout, `Scheduled: ${item.scheduled_at}`);
3768
+ if (item?.permalink) writeLine(context.stdout, `Permalink: ${item.permalink}`);
3769
+ if (item?.finalized_content) {
3770
+ writeLine(context.stdout, "");
3771
+ writeLine(context.stdout, item.finalized_content);
3772
+ }
3773
+ }
3774
+
3775
+ function renderContentComments(comments, context) {
3776
+ if (!Array.isArray(comments) || comments.length === 0) return writeLine(context.stdout, "No ContentOps comment tasks found.");
3777
+
3778
+ writeAlignedTable(context, ["TASK ID", "STATUS", "COMMENTER", "FIT", "OUTCOME", "COMMENT"], comments.map((comment) => [
3779
+ display(comment.prefix_id),
3780
+ display(comment.status),
3781
+ display(comment.commenter?.name),
3782
+ display(comment.fit_badge?.status || comment.fit_badge, "-"),
3783
+ display(comment.promotion_outcome, "-"),
3784
+ display(truncateCliText(comment.comment_body, 80))
3785
+ ]));
3786
+ }
3787
+
3463
3788
  function renderMotionStatus(status, context) {
3464
3789
  writeLine(context.stdout, `Motion: ${display(status?.name)} (${display(status?.prefix_id)})`);
3465
3790
  writeLine(context.stdout, `State: ${display(status?.state)}`);
@@ -3972,6 +4297,184 @@ function renderProspectImportStatus(payload, context) {
3972
4297
  writeLine(context.stdout, `Social profiles: ${socialCount}`);
3973
4298
  }
3974
4299
 
4300
+ function renderLinkedinReviewStarted(payload, context) {
4301
+ renderLinkedinReviewStatus(payload, context, { title: "LinkedIn review queued" });
4302
+ const reportId = payload?.report?.prefix_id;
4303
+ if (reportId) writeLine(context.stdout, `Run \`audienti tools linkedin-review status ${reportId}\` to check progress.`);
4304
+ }
4305
+
4306
+ function renderToolsList(tools, context) {
4307
+ writeAlignedTable(context, ["TOOL", "COMMAND", "REPORTS"], tools.map((tool) => [
4308
+ tool.id,
4309
+ tool.command,
4310
+ tool.reports_command || "-"
4311
+ ]));
4312
+ }
4313
+
4314
+ function renderLinkedinReviewReports(payload, context) {
4315
+ const rows = Array.isArray(payload?.reports) ? payload.reports : [];
4316
+ if (rows.length === 0) {
4317
+ writeLine(context.stdout, "No LinkedIn review reports found.");
4318
+ return;
4319
+ }
4320
+
4321
+ writeAlignedTable(context, ["REPORT ID", "STATUS", "STAGE", "PROFILE", "UPDATED"], rows.map(linkedinReviewReportRow));
4322
+ writeLine(context.stdout, "");
4323
+ writeLine(context.stdout, "Inspect one report with `audienti tools linkedin-review status <rprt_id>`.");
4324
+ }
4325
+
4326
+ function linkedinReviewReportRow(entry) {
4327
+ const report = entry?.report || {};
4328
+ const profile = entry?.profile || {};
4329
+
4330
+ return [
4331
+ display(report.prefix_id),
4332
+ display(report.display_status || report.status),
4333
+ display(report.stage),
4334
+ display(profile.display_name || profile.url || profile.username || report.title || report.input_url),
4335
+ display(report.updated_at)
4336
+ ];
4337
+ }
4338
+
4339
+ function renderLinkedinReviewStatus(payload, context, { title = "LinkedIn review status" } = {}) {
4340
+ const report = payload?.report || {};
4341
+ const profile = payload?.profile || {};
4342
+ const run = payload?.run || {};
4343
+ const queue = payload?.queue || {};
4344
+ const icp = payload?.icp || {};
4345
+
4346
+ writeLine(context.stdout, `${title}: ${display(report.prefix_id)}`);
4347
+ writeLine(context.stdout, `Status: ${display(report.display_status || report.status)}`);
4348
+ if (report.stage) writeLine(context.stdout, `Stage: ${report.stage}`);
4349
+ if (profile.url) writeLine(context.stdout, `Profile: ${profile.url}`);
4350
+ if (profile.display_name) writeLine(context.stdout, `Profile name: ${profile.display_name}`);
4351
+ if (icp.name || icp.id) writeLine(context.stdout, `ICP: ${display(icp.name)} (${display(icp.id)})`);
4352
+ if (run.status) writeLine(context.stdout, `Run: ${run.status}`);
4353
+ if (queue.state && queue.state !== "none") {
4354
+ writeLine(context.stdout, `Queue: ${queue.state} (${display(queue.pending_count, 0)} pending)`);
4355
+ }
4356
+ if (report.updated_at) writeLine(context.stdout, `Updated: ${report.updated_at}`);
4357
+ if (report.completed_at) writeLine(context.stdout, `Completed: ${report.completed_at}`);
4358
+ if (report.url) writeLine(context.stdout, `URL: ${report.url}`);
4359
+ }
4360
+
4361
+ function renderLinkedinReviewReport(payload, context) {
4362
+ renderLinkedinReviewStatus(payload, context, { title: "LinkedIn review report" });
4363
+
4364
+ const report = payload?.report || {};
4365
+ const content = payload?.content?.payload || {};
4366
+ if (!content || Object.keys(content).length === 0) {
4367
+ writeLine(context.stdout, "");
4368
+ writeLine(context.stdout, report.display_status === "completed" || report.status === "completed"
4369
+ ? "No report content is available yet."
4370
+ : "Report content is not available until the review completes.");
4371
+ return;
4372
+ }
4373
+
4374
+ const observed = objectValue(content.observed_profile);
4375
+ const scores = objectValue(content.scores);
4376
+ const findings = objectValue(content.findings);
4377
+ const strategy = objectValue(content.strategy);
4378
+ const rewrite = objectValue(content.rewrite);
4379
+ const leadMagnets = Array.isArray(content.lead_magnets) ? content.lead_magnets : [];
4380
+
4381
+ writeSection(context, "Summary");
4382
+ writeField(context, "Name", observed.name || report.title);
4383
+ writeField(context, "Headline", observed.headline);
4384
+ writeField(context, "Location", observed.location);
4385
+ writeField(context, "Authority score", scores.authority_score);
4386
+ writeField(context, "Score summary", scores.summary);
4387
+ writeField(context, "Bottom line", findings.bottom_line);
4388
+
4389
+ writeSection(context, "Strategy");
4390
+ writeField(context, "Buyer persona", strategy.buyer_persona);
4391
+ writeField(context, "Strategic gap", strategy.strategic_gap);
4392
+ writeField(context, "Opportunity", strategy.strategic_opportunity);
4393
+ writeField(context, "Fit", strategy.fit_assessment);
4394
+ writeBullets(context, "Next steps", strategy.next_steps);
4395
+
4396
+ writeSection(context, "Recommended Rewrite");
4397
+ writeField(context, "Headline", rewrite.recommended_headline);
4398
+ writeField(context, "Why", rewrite.recommended_reason);
4399
+ writeField(context, "Bio", rewrite.recommended_bio);
4400
+ writeBullets(context, "Improvement suggestions", rewrite.improvement_suggestions);
4401
+
4402
+ writeSection(context, "Findings");
4403
+ writeBullets(context, "What's working", Array.isArray(findings.whats_working) ? findings.whats_working.map(summaryItem) : []);
4404
+ writeBullets(context, "Revenue leaks", Array.isArray(findings.revenue_leaks) ? findings.revenue_leaks.map(summaryItem) : []);
4405
+
4406
+ writeSection(context, "Lead Magnets");
4407
+ if (leadMagnets.length === 0) {
4408
+ writeLine(context.stdout, "None");
4409
+ } else {
4410
+ leadMagnets.slice(0, 5).forEach((magnet, index) => {
4411
+ const headline = display(magnet?.headline || magnet?.title, `Idea ${index + 1}`);
4412
+ const contentType = magnet?.content_type ? ` (${magnet.content_type})` : "";
4413
+ writeLine(context.stdout, `${index + 1}. ${headline}${contentType}`);
4414
+ if (magnet?.subheadline) writeLine(context.stdout, ` ${magnet.subheadline}`);
4415
+ if (magnet?.description) writeLine(context.stdout, ` ${magnet.description}`);
4416
+ });
4417
+ }
4418
+ }
4419
+
4420
+ function objectValue(value) {
4421
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
4422
+ }
4423
+
4424
+ function writeSection(context, title) {
4425
+ writeLine(context.stdout, "");
4426
+ writeLine(context.stdout, title);
4427
+ writeLine(context.stdout, "-".repeat(title.length));
4428
+ }
4429
+
4430
+ function writeField(context, label, value) {
4431
+ const text = String(display(value)).trim();
4432
+ if (!text) return;
4433
+
4434
+ writeLine(context.stdout, `${label}: ${text}`);
4435
+ }
4436
+
4437
+ function writeBullets(context, label, values) {
4438
+ const rows = Array.isArray(values) ? values.map((value) => String(display(value)).trim()).filter(Boolean) : [];
4439
+ if (rows.length === 0) return;
4440
+
4441
+ writeLine(context.stdout, `${label}:`);
4442
+ rows.forEach((row) => writeLine(context.stdout, `- ${row}`));
4443
+ }
4444
+
4445
+ function summaryItem(item) {
4446
+ if (!item || typeof item !== "object") return item;
4447
+
4448
+ return [item.title, item.description].map((value) => String(display(value)).trim()).filter(Boolean).join(": ");
4449
+ }
4450
+
4451
+ function availableTools() {
4452
+ return [
4453
+ {
4454
+ id: "get-email",
4455
+ command: "audienti tools get email --url <linkedin_url>",
4456
+ description: "Find the first selected email for a LinkedIn person URL.",
4457
+ reports_command: null,
4458
+ status_command: null
4459
+ },
4460
+ {
4461
+ id: "get-phone",
4462
+ command: "audienti tools get phone --url <linkedin_url>",
4463
+ description: "Find the first selected phone for a LinkedIn person URL.",
4464
+ reports_command: null,
4465
+ status_command: null
4466
+ },
4467
+ {
4468
+ id: "linkedin-review",
4469
+ command: "audienti tools linkedin-review --url <linkedin_url> [--icp <icp_id>]",
4470
+ description: "Create a LinkedIn personal profile authority review and ICP-fit positioning blueprint.",
4471
+ reports_command: "audienti tools linkedin-review reports",
4472
+ status_command: "audienti tools linkedin-review status <rprt_id>",
4473
+ show_command: "audienti tools linkedin-review show <rprt_id>"
4474
+ }
4475
+ ];
4476
+ }
4477
+
3975
4478
  function writeProspectImportProspectLine(payload, context) {
3976
4479
  const prospect = payload?.prospect;
3977
4480
  if (!prospect) return;
@@ -4827,7 +5330,7 @@ const HELP_TOPICS = new Map([
4827
5330
  " audienti motions run-discovery <motn_id>",
4828
5331
  " audienti motions prospects <motn_id>",
4829
5332
  " audienti motions create --payload <file.json>",
4830
- " audienti motions update <motn_id> [--status <state>] [--tags <tag[,tag...]>]",
5333
+ " audienti motions update <motn_id> [--status <state>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>]",
4831
5334
  " audienti motions add-tag <motn_id> <tag>",
4832
5335
  " audienti motions remove-tag <motn_id> <tag>",
4833
5336
  " audienti motions activate <motn_id>",
@@ -4837,6 +5340,15 @@ const HELP_TOPICS = new Map([
4837
5340
  " audienti motions move-prospects <source_motn_id> --target <target_motn_id> <prsp_id> [prsp_id...]",
4838
5341
  " Tip: `plays` is accepted anywhere `motions` is accepted.",
4839
5342
  "",
5343
+ " ContentOps",
5344
+ " audienti content programs",
5345
+ " audienti content plan <cprg_id>",
5346
+ " audienti content show <cpwi_id>",
5347
+ " audienti content feedback <cpwi_id> --message <text>",
5348
+ " audienti content approve <cpwi_id>",
5349
+ " audienti content publish <cpwi_id> --url <permalink>",
5350
+ " audienti content comments",
5351
+ "",
4840
5352
  " Prospects",
4841
5353
  " audienti prospects list [filters]",
4842
5354
  " audienti prospects check [filters]",
@@ -4895,8 +5407,12 @@ const HELP_TOPICS = new Map([
4895
5407
  " audienti analytics content --window week",
4896
5408
  "",
4897
5409
  " Utilities",
5410
+ " audienti tools list",
4898
5411
  " audienti tools get email --url <linkedin_url>",
4899
5412
  " audienti tools get phone --url <linkedin_url>",
5413
+ " audienti tools linkedin-review --url <linkedin_url> [--icp <icp_id>]",
5414
+ " audienti tools linkedin-review reports",
5415
+ " audienti tools linkedin-review show <rprt_id>",
4900
5416
  "",
4901
5417
  "Common flows:",
4902
5418
  " Work the next move: audienti operator next --plan",
@@ -5907,7 +6423,7 @@ const HELP_TOPICS = new Map([
5907
6423
  " audienti motions prospects <motn_id> [--json]",
5908
6424
  " audienti motions add-prospects <motn_id> <prsp_id> [prsp_id...] [--json]",
5909
6425
  " audienti motions create --payload <file.json> [--json]",
5910
- " audienti motions update <motn_id> [--status <draft|preparing|active|paused|archived>] [--tags <tag[,tag...]>] [--json]",
6426
+ " audienti motions update <motn_id> [--status <draft|preparing|active|paused|archived>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>] [--json]",
5911
6427
  " audienti motions add-tag <motn_id> <tag> [--json]",
5912
6428
  " audienti motions remove-tag <motn_id> <tag> [--json]",
5913
6429
  " audienti motions activate <motn_id> [--json]",
@@ -5926,6 +6442,34 @@ const HELP_TOPICS = new Map([
5926
6442
  " motn_id: motn_ prefix id"
5927
6443
  ].join("\n")],
5928
6444
 
6445
+ ["content", [
6446
+ "Usage:",
6447
+ " audienti content programs [--user <account_user_id|email|name|me>] [--json]",
6448
+ " audienti content plan <cprg_id> [--week <n>] [--due] [--json]",
6449
+ " audienti content show <cpwi_id> [--json]",
6450
+ " audienti content feedback <cpwi_id> --message <text> [--json]",
6451
+ " audienti content approve <cpwi_id> [--json]",
6452
+ " audienti content schedule <cpwi_id> --at <time> [--json]",
6453
+ " audienti content publish <cpwi_id> --url <permalink> [--json]",
6454
+ " audienti content comments [--unresolved] [--user <account_user_id|email|name|me>] [--json]",
6455
+ " audienti content reply <cctk_id> [--body <text>] [--json]",
6456
+ " audienti content dismiss <cctk_id> [--json]",
6457
+ "",
6458
+ "API:",
6459
+ " GET/POST /api/v1/accounts/:account_id/content_ops/..."
6460
+ ].join("\n")],
6461
+
6462
+ ["content programs", [CONTENT_PROGRAMS_USAGE].join("\n")],
6463
+ ["content plan", [CONTENT_PLAN_USAGE].join("\n")],
6464
+ ["content show", [CONTENT_SHOW_USAGE].join("\n")],
6465
+ ["content feedback", [CONTENT_FEEDBACK_USAGE].join("\n")],
6466
+ ["content approve", [CONTENT_APPROVE_USAGE].join("\n")],
6467
+ ["content schedule", [CONTENT_SCHEDULE_USAGE].join("\n")],
6468
+ ["content publish", [CONTENT_PUBLISH_USAGE].join("\n")],
6469
+ ["content comments", [CONTENT_COMMENTS_USAGE].join("\n")],
6470
+ ["content reply", [CONTENT_REPLY_USAGE].join("\n")],
6471
+ ["content dismiss", [CONTENT_DISMISS_USAGE].join("\n")],
6472
+
5929
6473
  ["motions list", [
5930
6474
  "Usage:",
5931
6475
  " audienti motions list [--tag <tag>] [--json] [--account <acct_id>]",
@@ -6925,12 +7469,36 @@ const HELP_TOPICS = new Map([
6925
7469
 
6926
7470
  ["tools", [
6927
7471
  "Usage:",
7472
+ " audienti tools list [--json]",
6928
7473
  " audienti tools get <email|phone> --url <linkedin_url> [--json]",
7474
+ " audienti tools linkedin-review --url <linkedin_url> [--icp <icp_id>] [--json]",
7475
+ " audienti tools linkedin-review reports [--limit <n>] [--json]",
7476
+ " audienti tools linkedin-review show <rprt_id> [--json]",
7477
+ " audienti tools linkedin-review status <rprt_id> [--json]",
6929
7478
  "",
6930
7479
  "Status: implemented",
6931
7480
  "",
6932
7481
  "Commands:",
6933
- " audienti tools get Run a LinkedIn URL through the existing import and contact-enrichment pipeline, then return the first selected email or phone."
7482
+ " audienti tools list Show available CLI tools and the report commands they support.",
7483
+ " audienti tools get Run a LinkedIn URL through the existing import and contact-enrichment pipeline, then return the first selected email or phone.",
7484
+ " audienti tools linkedin-review Queue a LinkedIn personal profile authority review and ICP-fit positioning blueprint.",
7485
+ " audienti tools linkedin-review reports List recent LinkedIn Review reports for the active account.",
7486
+ " audienti tools linkedin-review show View the completed report content in the terminal.",
7487
+ " audienti tools linkedin-review status Show the current report stage and run status."
7488
+ ].join("\n")],
7489
+
7490
+ ["tools list", [
7491
+ "Usage:",
7492
+ ` ${TOOLS_LIST_USAGE.slice("Usage: ".length)}`,
7493
+ "",
7494
+ "Status: implemented",
7495
+ "",
7496
+ "Purpose:",
7497
+ " Shows the available CLI-backed tools and which commands create or inspect reports.",
7498
+ "",
7499
+ "Output:",
7500
+ " Plain text: tool id, create command, and reports command",
7501
+ " JSON: { tools }"
6934
7502
  ].join("\n")],
6935
7503
 
6936
7504
  ["tools get", [
@@ -6959,6 +7527,98 @@ const HELP_TOPICS = new Map([
6959
7527
  " JSON: { kind, url, found, value, import_id, status, ready, prospect, pipeline }"
6960
7528
  ].join("\n")],
6961
7529
 
7530
+ ["tools linkedin-review", [
7531
+ "Usage:",
7532
+ " audienti tools linkedin-review --url <linkedin_url> [--icp <icp_id>] [--json] [--account <acct_id>]",
7533
+ " audienti tools linkedin-review reports [--limit <n>] [--json] [--account <acct_id>]",
7534
+ " audienti tools linkedin-review show <rprt_id> [--json] [--account <acct_id>]",
7535
+ " audienti tools linkedin-review status <rprt_id> [--json] [--account <acct_id>]",
7536
+ "",
7537
+ "Status: implemented",
7538
+ "",
7539
+ "Purpose:",
7540
+ " Queues the same LinkedIn Review / Blueprint report as the web tool for a LinkedIn person profile.",
7541
+ "",
7542
+ "Input shape:",
7543
+ " linkedin_url: url LinkedIn person profile URL, not a company URL",
7544
+ " icp_id: icpp_ id or numeric id | optional buyer context for positioning rewrites",
7545
+ "",
7546
+ "Behavior:",
7547
+ " Creates a profile-backed LinkedIn Blueprint report, starts profile enrichment when needed, and queues the authority review once the profile is ready.",
7548
+ " The command returns the report URL and status command immediately; report generation continues in Audienti.",
7549
+ "",
7550
+ "Options:",
7551
+ " --url <linkedin_url> LinkedIn person profile URL",
7552
+ " --icp <icp_id> Optional ICP to tune positioning recommendations",
7553
+ "",
7554
+ "Output:",
7555
+ " Plain text: queued report id, status, profile, ICP, queue state, and product URL",
7556
+ " JSON: { report, profile, run, queue, icp, queued }",
7557
+ "",
7558
+ "API:",
7559
+ " POST /api/v1/accounts/:account_id/tools/linkedin-review/reports.json"
7560
+ ].join("\n")],
7561
+
7562
+ ["tools linkedin-review reports", [
7563
+ "Usage:",
7564
+ ` ${TOOLS_LINKEDIN_REVIEW_REPORTS_USAGE.slice("Usage: ".length)}`,
7565
+ "",
7566
+ "Status: implemented",
7567
+ "",
7568
+ "Purpose:",
7569
+ " Lists recent LinkedIn Review reports for the active account so you can find report ids and inspect progress.",
7570
+ "",
7571
+ "Options:",
7572
+ " --limit <n> Number of recent reports to return. Default: 20, max: 100",
7573
+ "",
7574
+ "Output:",
7575
+ " Plain text: report id, status, stage, profile, and updated timestamp",
7576
+ " JSON: { reports, count, limit }",
7577
+ "",
7578
+ "API:",
7579
+ " GET /api/v1/accounts/:account_id/tools/linkedin-review/reports.json"
7580
+ ].join("\n")],
7581
+
7582
+ ["tools linkedin-review show", [
7583
+ "Usage:",
7584
+ ` ${TOOLS_LINKEDIN_REVIEW_SHOW_USAGE.slice("Usage: ".length)}`,
7585
+ "",
7586
+ "Status: implemented",
7587
+ "",
7588
+ "Purpose:",
7589
+ " Prints the completed LinkedIn Review report content in the terminal.",
7590
+ "",
7591
+ "Input shape:",
7592
+ " rprt_id: rprt_ report id from `audienti tools linkedin-review reports`",
7593
+ "",
7594
+ "Output:",
7595
+ " Plain text: summary, strategy, rewrite, findings, lead magnets, and report URL",
7596
+ " JSON: { report, profile, run, queue, icp, content }",
7597
+ "",
7598
+ "API:",
7599
+ " GET /api/v1/accounts/:account_id/tools/linkedin-review/reports/:id.json"
7600
+ ].join("\n")],
7601
+
7602
+ ["tools linkedin-review status", [
7603
+ "Usage:",
7604
+ " audienti tools linkedin-review status <rprt_id> [--json] [--account <acct_id>]",
7605
+ "",
7606
+ "Status: implemented",
7607
+ "",
7608
+ "Purpose:",
7609
+ " Shows whether a LinkedIn Review report is waiting on enrichment, running, completed, or failed.",
7610
+ "",
7611
+ "Input shape:",
7612
+ " rprt_id: rprt_ report id returned by `audienti tools linkedin-review`",
7613
+ "",
7614
+ "Output:",
7615
+ " Plain text: report status, stage, run status, queue state, timestamps, and product URL",
7616
+ " JSON: { report, profile, run, queue, icp, queued }",
7617
+ "",
7618
+ "API:",
7619
+ " GET /api/v1/accounts/:account_id/tools/linkedin-review/reports/:id.json"
7620
+ ].join("\n")],
7621
+
6962
7622
  ["operator", [
6963
7623
  "Usage:",
6964
7624
  " audienti operator next [--json|--plan|--done|--skip|--fail|--return]",