@vm0/cli 9.87.0 → 9.88.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -60,7 +60,7 @@ import {
60
60
  showNextSteps,
61
61
  volumeConfigSchema,
62
62
  withErrorHandler
63
- } from "./chunk-JOVCRTRX.js";
63
+ } from "./chunk-NM4FYPVK.js";
64
64
 
65
65
  // src/index.ts
66
66
  import { Command as Command44 } from "commander";
@@ -445,7 +445,7 @@ function getConfigPath() {
445
445
  return join(homedir(), ".vm0", "config.json");
446
446
  }
447
447
  var infoCommand = new Command6().name("info").description("Display environment and debug information").action(async () => {
448
- console.log(chalk3.bold(`VM0 CLI v${"9.87.0"}`));
448
+ console.log(chalk3.bold(`VM0 CLI v${"9.88.0"}`));
449
449
  console.log();
450
450
  const config = await loadConfig();
451
451
  const hasEnvToken = !!process.env.VM0_TOKEN;
@@ -1548,7 +1548,7 @@ var composeCommand = new Command7().name("compose").description("Create or updat
1548
1548
  options.autoUpdate = false;
1549
1549
  }
1550
1550
  if (options.autoUpdate !== false) {
1551
- await startSilentUpgrade("9.87.0");
1551
+ await startSilentUpgrade("9.88.0");
1552
1552
  }
1553
1553
  try {
1554
1554
  let result;
@@ -1625,7 +1625,7 @@ var mainRunCommand = new Command8().name("run").description("Run an agent").argu
1625
1625
  withErrorHandler(
1626
1626
  async (identifier, prompt, options) => {
1627
1627
  if (options.autoUpdate !== false) {
1628
- await startSilentUpgrade("9.87.0");
1628
+ await startSilentUpgrade("9.88.0");
1629
1629
  }
1630
1630
  const { org, name, version } = parseIdentifier(identifier);
1631
1631
  let composeId;
@@ -3394,7 +3394,7 @@ var cookAction = new Command35().name("cook").description("Quick start: prepare,
3394
3394
  withErrorHandler(
3395
3395
  async (prompt, options) => {
3396
3396
  if (options.autoUpdate !== false) {
3397
- const shouldExit = await checkAndUpgrade("9.87.0", prompt);
3397
+ const shouldExit = await checkAndUpgrade("9.88.0", prompt);
3398
3398
  if (shouldExit) {
3399
3399
  process.exit(0);
3400
3400
  }
@@ -4134,13 +4134,13 @@ var upgradeCommand = new Command42().name("upgrade").description("Upgrade vm0 CL
4134
4134
  if (latestVersion === null) {
4135
4135
  throw new Error("Could not check for updates. Please try again later.");
4136
4136
  }
4137
- if (latestVersion === "9.87.0") {
4138
- console.log(chalk33.green(`\u2713 Already up to date (${"9.87.0"})`));
4137
+ if (latestVersion === "9.88.0") {
4138
+ console.log(chalk33.green(`\u2713 Already up to date (${"9.88.0"})`));
4139
4139
  return;
4140
4140
  }
4141
4141
  console.log(
4142
4142
  chalk33.yellow(
4143
- `Current version: ${"9.87.0"} -> Latest version: ${latestVersion}`
4143
+ `Current version: ${"9.88.0"} -> Latest version: ${latestVersion}`
4144
4144
  )
4145
4145
  );
4146
4146
  console.log();
@@ -4167,7 +4167,7 @@ var upgradeCommand = new Command42().name("upgrade").description("Upgrade vm0 CL
4167
4167
  const success = await performUpgrade(packageManager);
4168
4168
  if (success) {
4169
4169
  console.log(
4170
- chalk33.green(`\u2713 Upgraded from ${"9.87.0"} to ${latestVersion}`)
4170
+ chalk33.green(`\u2713 Upgraded from ${"9.88.0"} to ${latestVersion}`)
4171
4171
  );
4172
4172
  return;
4173
4173
  }
@@ -4235,7 +4235,7 @@ var whoamiCommand = new Command43().name("whoami").description("Show current ide
4235
4235
 
4236
4236
  // src/index.ts
4237
4237
  var program = new Command44();
4238
- program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("9.87.0");
4238
+ program.name("vm0").description("VM0 CLI - Build and run agents with natural language").version("9.88.0");
4239
4239
  program.addCommand(authCommand);
4240
4240
  program.addCommand(infoCommand);
4241
4241
  program.addCommand(composeCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vm0/cli",
3
- "version": "9.87.0",
3
+ "version": "9.88.0",
4
4
  "description": "CLI application",
5
5
  "repository": {
6
6
  "type": "git",
package/zero.js CHANGED
@@ -42,6 +42,7 @@ import {
42
42
  getToken,
43
43
  getZeroAgent,
44
44
  getZeroAgentInstructions,
45
+ getZeroAgentUserConnectors,
45
46
  getZeroConnector,
46
47
  getZeroConnectorSession,
47
48
  getZeroOrg,
@@ -78,6 +79,7 @@ import {
78
79
  resolveZeroScheduleByAgent,
79
80
  saveConfig,
80
81
  sendSlackMessage,
82
+ setZeroAgentUserConnectors,
81
83
  setZeroOrgModelProviderDefault,
82
84
  setZeroOrgSecret,
83
85
  setZeroOrgVariable,
@@ -91,7 +93,7 @@ import {
91
93
  updateZeroUserPreferences,
92
94
  upsertZeroOrgModelProvider,
93
95
  withErrorHandler
94
- } from "./chunk-JOVCRTRX.js";
96
+ } from "./chunk-NM4FYPVK.js";
95
97
 
96
98
  // src/zero.ts
97
99
  import { Command as Command61 } from "commander";
@@ -1068,9 +1070,9 @@ import { Command as Command29 } from "commander";
1068
1070
  import { Command as Command24 } from "commander";
1069
1071
  import { readFileSync } from "fs";
1070
1072
  import chalk21 from "chalk";
1071
- var createCommand = new Command24().name("create").description("Create a new zero agent").requiredOption(
1073
+ var createCommand = new Command24().name("create").description("Create a new zero agent").option(
1072
1074
  "--connectors <items>",
1073
- "Comma-separated connector short names (e.g. github,linear)"
1075
+ "Comma-separated connector types to enable for this agent (e.g. github,linear)"
1074
1076
  ).option("--display-name <name>", "Agent display name").option("--description <text>", "Agent description").option(
1075
1077
  "--sound <tone>",
1076
1078
  "Agent tone: professional, friendly, direct, supportive"
@@ -1078,26 +1080,30 @@ var createCommand = new Command24().name("create").description("Create a new zer
1078
1080
  "after",
1079
1081
  `
1080
1082
  Examples:
1081
- Minimal: zero agent create --connectors github
1082
- With display name: zero agent create --connectors github,linear --display-name "My Agent"
1083
+ Minimal: zero agent create --display-name "My Agent"
1084
+ With connectors: zero agent create --connectors github,linear --display-name "My Agent"
1083
1085
  With instructions: zero agent create --connectors github --instructions-file ./instructions.md`
1084
1086
  ).action(
1085
1087
  withErrorHandler(
1086
1088
  async (options) => {
1087
- const connectors = options.connectors.split(",").map((s) => s.trim());
1088
1089
  const agent = await createZeroAgent({
1089
- connectors,
1090
1090
  displayName: options.displayName,
1091
1091
  description: options.description,
1092
1092
  sound: options.sound
1093
1093
  });
1094
+ if (options.connectors) {
1095
+ const connectors = options.connectors.split(",").map((s) => s.trim());
1096
+ await setZeroAgentUserConnectors(agent.agentId, connectors);
1097
+ }
1094
1098
  if (options.instructionsFile) {
1095
1099
  const content = readFileSync(options.instructionsFile, "utf-8");
1096
1100
  await updateZeroAgentInstructions(agent.agentId, content);
1097
1101
  }
1098
1102
  console.log(chalk21.green(`\u2713 Agent "${agent.agentId}" created`));
1099
1103
  console.log(` Agent ID: ${agent.agentId}`);
1100
- console.log(` Connectors: ${agent.connectors.join(", ")}`);
1104
+ if (options.connectors) {
1105
+ console.log(` Connectors: ${options.connectors}`);
1106
+ }
1101
1107
  if (agent.displayName) {
1102
1108
  console.log(` Display Name: ${agent.displayName}`);
1103
1109
  }
@@ -1109,10 +1115,7 @@ Examples:
1109
1115
  import { Command as Command25 } from "commander";
1110
1116
  import { readFileSync as readFileSync2 } from "fs";
1111
1117
  import chalk22 from "chalk";
1112
- var editCommand = new Command25().name("edit").description("Edit a zero agent").argument("<agent-id>", "Agent ID").option(
1113
- "--connectors <items>",
1114
- "Comma-separated connector short names (e.g. github,linear)"
1115
- ).option("--display-name <name>", "New display name").option("--description <text>", "New description").option(
1118
+ var editCommand = new Command25().name("edit").description("Edit a zero agent").argument("<agent-id>", "Agent ID").option("--display-name <name>", "New display name").option("--description <text>", "New description").option(
1116
1119
  "--sound <tone>",
1117
1120
  "New tone: professional, friendly, direct, supportive"
1118
1121
  ).option("--instructions-file <path>", "Path to new instructions file").addHelpText(
@@ -1130,17 +1133,15 @@ Notes:
1130
1133
  ).action(
1131
1134
  withErrorHandler(
1132
1135
  async (agentId, options) => {
1133
- const hasAgentUpdate = options.connectors !== void 0 || options.displayName !== void 0 || options.description !== void 0 || options.sound !== void 0;
1136
+ const hasAgentUpdate = options.displayName !== void 0 || options.description !== void 0 || options.sound !== void 0;
1134
1137
  if (!hasAgentUpdate && !options.instructionsFile) {
1135
1138
  throw new Error(
1136
- "At least one option is required (--connectors, --display-name, --description, --sound, --instructions-file)"
1139
+ "At least one option is required (--display-name, --description, --sound, --instructions-file)"
1137
1140
  );
1138
1141
  }
1139
1142
  if (hasAgentUpdate) {
1140
1143
  const current = await getZeroAgent(agentId);
1141
- const connectors = options.connectors ? options.connectors.split(",").map((s) => s.trim()) : current.connectors;
1142
1144
  await updateZeroAgent(agentId, {
1143
- connectors,
1144
1145
  displayName: options.displayName !== void 0 ? options.displayName : current.displayName ?? void 0,
1145
1146
  description: options.description !== void 0 ? options.description : current.description ?? void 0,
1146
1147
  sound: options.sound !== void 0 ? options.sound : current.sound ?? void 0
@@ -1173,7 +1174,9 @@ Examples:
1173
1174
  if (agent.displayName) console.log(chalk23.dim(agent.displayName));
1174
1175
  console.log();
1175
1176
  console.log(`Agent ID: ${agent.agentId}`);
1176
- console.log(`Connectors: ${agent.connectors.join(", ") || "-"}`);
1177
+ const connectors = await getZeroAgentUserConnectors(agentId);
1178
+ if (connectors.length > 0)
1179
+ console.log(`Connectors: ${connectors.join(", ")}`);
1177
1180
  if (agent.description)
1178
1181
  console.log(`Description: ${agent.description}`);
1179
1182
  if (agent.sound) console.log(`Sound: ${agent.sound}`);
@@ -1209,7 +1212,7 @@ Notes:
1209
1212
  console.log(chalk24.dim("No zero agents found"));
1210
1213
  console.log(
1211
1214
  chalk24.dim(
1212
- ' Create one with: zero agent create --connectors github --display-name "My Agent"'
1215
+ ' Create one with: zero agent create --display-name "My Agent"'
1213
1216
  )
1214
1217
  );
1215
1218
  return;
@@ -1221,15 +1224,13 @@ Notes:
1221
1224
  );
1222
1225
  const header = [
1223
1226
  "AGENT ID".padEnd(idWidth),
1224
- "DISPLAY NAME".padEnd(displayWidth),
1225
- "CONNECTORS"
1227
+ "DISPLAY NAME".padEnd(displayWidth)
1226
1228
  ].join(" ");
1227
1229
  console.log(chalk24.dim(header));
1228
1230
  for (const agent of agents) {
1229
1231
  const row = [
1230
1232
  agent.agentId.padEnd(idWidth),
1231
- (agent.displayName ?? "-").padEnd(displayWidth),
1232
- agent.connectors.join(", ") || "-"
1233
+ (agent.displayName ?? "-").padEnd(displayWidth)
1233
1234
  ].join(" ");
1234
1235
  console.log(row);
1235
1236
  }
@@ -1892,6 +1893,19 @@ import { Command as Command36 } from "commander";
1892
1893
 
1893
1894
  // src/commands/zero/doctor/missing-token.ts
1894
1895
  import { Command as Command35 } from "commander";
1896
+ function toPlatformUrl(apiUrl) {
1897
+ const parsed = new URL(apiUrl);
1898
+ const parts = parsed.hostname.split(".");
1899
+ if (parts[0].endsWith("-www")) {
1900
+ parts[0] = parts[0].slice(0, -"-www".length) + "-app";
1901
+ } else if (parts[0] === "www" || parts[0] === "platform") {
1902
+ parts[0] = "app";
1903
+ } else if (parts[0] !== "app" && parts[0] !== "localhost") {
1904
+ parts.unshift("app");
1905
+ }
1906
+ parsed.hostname = parts.join(".");
1907
+ return parsed;
1908
+ }
1895
1909
  var missingTokenCommand = new Command35().name("missing-token").description(
1896
1910
  "Diagnose a missing token and find the connector that provides it"
1897
1911
  ).argument("<token-name>", "The environment variable / token name to look up").addHelpText(
@@ -1915,19 +1929,32 @@ Notes:
1915
1929
  }
1916
1930
  const { label } = CONNECTOR_TYPES[connectorType];
1917
1931
  const apiUrl = await getApiUrl();
1918
- const parsed = new URL(apiUrl);
1919
- const parts = parsed.hostname.split(".");
1920
- if (parts[0] === "www" || parts[0] === "platform") {
1921
- parts[0] = "app";
1922
- } else if (parts[0] !== "app" && parts[0] !== "localhost") {
1923
- parts.unshift("app");
1924
- }
1925
- parsed.hostname = parts.join(".");
1932
+ const platformUrl = toPlatformUrl(apiUrl);
1926
1933
  const agentId = process.env.ZERO_AGENT_ID;
1927
- const path = agentId ? `/team/${agentId}` : "/team";
1928
- const url = `${parsed.origin}${path}?tab=connectors`;
1934
+ const [connector, enabledTypes] = await Promise.all([
1935
+ getZeroConnector(connectorType).catch(() => null),
1936
+ agentId ? getZeroAgentUserConnectors(agentId).catch(() => null) : Promise.resolve(null)
1937
+ ]);
1938
+ const isConnected = connector !== null;
1939
+ const hasPermission = enabledTypes !== null && enabledTypes.includes(connectorType);
1929
1940
  console.log(`${tokenName} is provided by the ${label} connector.`);
1930
- console.log(`Ask the user to connect it at: ${url}`);
1941
+ if (!isConnected) {
1942
+ const url = `${platformUrl.origin}/connectors`;
1943
+ console.log(
1944
+ `The ${label} connector is not connected. Ask the user to connect it at: ${url}`
1945
+ );
1946
+ } else if (!hasPermission) {
1947
+ const path = agentId ? `/team/${agentId}` : "/team";
1948
+ const url = `${platformUrl.origin}${path}?tab=authorization`;
1949
+ console.log(
1950
+ `The ${label} connector is connected but not authorized for this agent. Ask the user to enable it at: ${url}`
1951
+ );
1952
+ } else {
1953
+ const url = `${platformUrl.origin}/connectors`;
1954
+ console.log(
1955
+ `The ${label} connector is connected and authorized, but the token is still missing. Ask the user to check the connector status at: ${url}`
1956
+ );
1957
+ }
1931
1958
  })
1932
1959
  );
1933
1960
 
@@ -2452,28 +2479,41 @@ async function gatherPromptText(optionPrompt, existingPrompt) {
2452
2479
  existingPrompt || "let's start working."
2453
2480
  );
2454
2481
  }
2455
- async function gatherNotificationPreferences(optionNotifyEmail, optionNotifySlack, existingSchedule) {
2482
+ async function gatherNotificationPreferences(optionNotifyEmail, optionNotifySlack, optionNotifySlackChannelId, existingSchedule) {
2456
2483
  if (optionNotifyEmail !== void 0 && optionNotifySlack !== void 0) {
2457
2484
  return {
2458
2485
  notifyEmail: optionNotifyEmail,
2459
- notifySlack: optionNotifySlack
2486
+ notifySlack: optionNotifySlack,
2487
+ notifySlackChannelId: optionNotifySlackChannelId
2460
2488
  };
2461
2489
  }
2462
2490
  if (!isInteractive()) {
2463
2491
  return {
2464
2492
  notifyEmail: optionNotifyEmail,
2465
- notifySlack: optionNotifySlack
2493
+ notifySlack: optionNotifySlack,
2494
+ notifySlackChannelId: optionNotifySlackChannelId
2466
2495
  };
2467
2496
  }
2468
2497
  const notifyEmail = optionNotifyEmail ?? await promptConfirm(
2469
2498
  "Enable email notifications?",
2470
- existingSchedule?.notifyEmail ?? true
2499
+ existingSchedule?.notifyEmail ?? false
2471
2500
  );
2472
2501
  const notifySlack = optionNotifySlack ?? await promptConfirm(
2473
2502
  "Enable Slack notifications?",
2474
- existingSchedule?.notifySlack ?? true
2503
+ existingSchedule?.notifySlack ?? false
2475
2504
  );
2476
- return { notifyEmail, notifySlack };
2505
+ let notifySlackChannelId = optionNotifySlackChannelId;
2506
+ if (notifySlackChannelId === void 0 && notifySlack) {
2507
+ const defaultChannel = existingSchedule?.notifySlackChannelId ?? "";
2508
+ const channelInput = await promptText(
2509
+ "Slack channel ID (leave empty for DM)",
2510
+ defaultChannel
2511
+ );
2512
+ if (channelInput) {
2513
+ notifySlackChannelId = channelInput;
2514
+ }
2515
+ }
2516
+ return { notifyEmail, notifySlack, notifySlackChannelId };
2477
2517
  }
2478
2518
  async function gatherInterval(optionInterval, existingInterval) {
2479
2519
  if (optionInterval) {
@@ -2568,12 +2608,14 @@ Deploying schedule for agent ${chalk33.cyan(params.agentName)}...`
2568
2608
  intervalSeconds: params.intervalSeconds,
2569
2609
  timezone: params.timezone,
2570
2610
  prompt: params.prompt,
2571
- artifactName: params.artifactName,
2572
2611
  ...params.notifyEmail !== void 0 && {
2573
2612
  notifyEmail: params.notifyEmail
2574
2613
  },
2575
2614
  ...params.notifySlack !== void 0 && {
2576
2615
  notifySlack: params.notifySlack
2616
+ },
2617
+ ...params.notifySlackChannelId !== void 0 && {
2618
+ notifySlackChannelId: params.notifySlackChannelId
2577
2619
  }
2578
2620
  });
2579
2621
  return deployResult;
@@ -2653,7 +2695,10 @@ async function handleScheduleEnabling(params) {
2653
2695
  showEnableHint(agentName);
2654
2696
  }
2655
2697
  }
2656
- var setupCommand2 = new Command40().name("setup").description("Create or edit a schedule for a zero agent").argument("<agent-id>", "Agent ID").option("-n, --name <schedule-name>", 'Schedule name (default: "default")').option("-f, --frequency <type>", "Frequency: daily|weekly|monthly|once|loop").option("-t, --time <HH:MM>", "Time to run (24-hour format)").option("-d, --day <day>", "Day of week (mon-sun) or day of month (1-31)").option("-i, --interval <seconds>", "Interval in seconds for loop mode").option("-z, --timezone <tz>", "IANA timezone").option("-p, --prompt <text>", "Prompt to run").option("--artifact-name <name>", "Artifact name", "artifact").option("-e, --enable", "Enable schedule immediately after creation").option("--notify-email", "Enable email notifications (default: true)").option("--no-notify-email", "Disable email notifications").option("--notify-slack", "Enable Slack notifications (default: true)").option("--no-notify-slack", "Disable Slack notifications").addHelpText(
2698
+ var setupCommand2 = new Command40().name("setup").description("Create or edit a schedule for a zero agent").argument("<agent-id>", "Agent ID").option("-n, --name <schedule-name>", 'Schedule name (default: "default")').option("-f, --frequency <type>", "Frequency: daily|weekly|monthly|once|loop").option("-t, --time <HH:MM>", "Time to run (24-hour format)").option("-d, --day <day>", "Day of week (mon-sun) or day of month (1-31)").option("-i, --interval <seconds>", "Interval in seconds for loop mode").option("-z, --timezone <tz>", "IANA timezone").option("-p, --prompt <text>", "Prompt to run").option("-e, --enable", "Enable schedule immediately after creation").option("--notify-email", "Enable email notifications (default: false)").option("--no-notify-email", "Disable email notifications").option("--notify-slack", "Enable Slack notifications (default: false)").option("--no-notify-slack", "Disable Slack notifications").option(
2699
+ "--notify-slack-channel-id <channel-id>",
2700
+ "Slack channel ID for notifications (default: DM)"
2701
+ ).addHelpText(
2657
2702
  "after",
2658
2703
  `
2659
2704
  Examples:
@@ -2667,7 +2712,8 @@ Examples:
2667
2712
  Notes:
2668
2713
  - Re-running setup with the same agent updates the existing "default" schedule
2669
2714
  - Use -n to manage multiple named schedules for the same agent
2670
- - All flags are required in non-interactive mode; interactive mode prompts for missing values`
2715
+ - All flags are required in non-interactive mode; interactive mode prompts for missing values
2716
+ - When --notify-slack is enabled, run results are automatically posted to the Slack channel specified by --notify-slack-channel-id (or as a DM if not set). No need to include Slack delivery instructions in your prompt.`
2671
2717
  ).action(
2672
2718
  withErrorHandler(async (agentIdentifier, options) => {
2673
2719
  const compose = await resolveCompose(agentIdentifier);
@@ -2717,9 +2763,10 @@ Notes:
2717
2763
  console.log(chalk33.dim("Cancelled"));
2718
2764
  return;
2719
2765
  }
2720
- const { notifyEmail, notifySlack } = await gatherNotificationPreferences(
2766
+ const { notifyEmail, notifySlack, notifySlackChannelId } = await gatherNotificationPreferences(
2721
2767
  options.notifyEmail,
2722
2768
  options.notifySlack,
2769
+ options.notifySlackChannelId,
2723
2770
  existingSchedule
2724
2771
  );
2725
2772
  const deployResult = await buildAndDeploy({
@@ -2733,9 +2780,9 @@ Notes:
2733
2780
  intervalSeconds,
2734
2781
  timezone,
2735
2782
  prompt: promptText_,
2736
- artifactName: options.artifactName,
2737
2783
  notifyEmail,
2738
- notifySlack
2784
+ notifySlack,
2785
+ notifySlackChannelId
2739
2786
  });
2740
2787
  displayDeployResult(scheduleName, deployResult);
2741
2788
  const shouldPromptEnable = deployResult.created || existingSchedule !== void 0 && !existingSchedule.enabled;
@@ -2841,10 +2888,6 @@ function printRunConfiguration(schedule) {
2841
2888
  if (schedule.secretNames && schedule.secretNames.length > 0) {
2842
2889
  console.log(`${"Secrets:".padEnd(16)}${schedule.secretNames.join(", ")}`);
2843
2890
  }
2844
- if (schedule.artifactName) {
2845
- const artifactInfo = schedule.artifactVersion ? `${schedule.artifactName}:${schedule.artifactVersion}` : schedule.artifactName;
2846
- console.log(`${"Artifact:".padEnd(16)}${artifactInfo}`);
2847
- }
2848
2891
  if (schedule.volumeVersions && Object.keys(schedule.volumeVersions).length > 0) {
2849
2892
  console.log(
2850
2893
  `${"Volumes:".padEnd(16)}${Object.keys(schedule.volumeVersions).join(", ")}`
@@ -3508,7 +3551,7 @@ function registerZeroCommands(prog, commands) {
3508
3551
  var program = new Command61();
3509
3552
  program.name("zero").description(
3510
3553
  "Zero CLI \u2014 interact with the zero platform from inside the sandbox"
3511
- ).version("9.87.0").addHelpText(
3554
+ ).version("9.88.0").addHelpText(
3512
3555
  "after",
3513
3556
  `
3514
3557
  Examples: