exponential-cli 1.12.0 → 1.14.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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command22 } from "commander";
4
+ import { Command as Command23 } from "commander";
5
5
 
6
6
  // src/commands/auth.ts
7
7
  import { Command } from "commander";
@@ -1358,6 +1358,157 @@ Pages (${pages.length} total)`));
1358
1358
  }
1359
1359
  console.log();
1360
1360
  }
1361
+ function meetingWhen(meeting) {
1362
+ return new Date(meeting.meetingDate ?? meeting.createdAt);
1363
+ }
1364
+ function renderMeetingSummary(summary) {
1365
+ if (!summary.trimStart().startsWith("{")) return summary;
1366
+ try {
1367
+ const parsed = JSON.parse(summary);
1368
+ if (parsed && typeof parsed === "object") {
1369
+ const obj = parsed;
1370
+ const parts = [obj.overview, obj.detailed_breakdown].filter(
1371
+ (v) => typeof v === "string" && v.length > 0
1372
+ );
1373
+ if (parts.length > 0) return parts.join("\n\n");
1374
+ }
1375
+ } catch {
1376
+ }
1377
+ return summary;
1378
+ }
1379
+ function transformMeeting(m) {
1380
+ return {
1381
+ id: m.id,
1382
+ sessionId: m.sessionId,
1383
+ title: m.title,
1384
+ description: m.description,
1385
+ notes: m.notes,
1386
+ summary: m.summary,
1387
+ transcription: m.transcription,
1388
+ meetingDate: m.meetingDate,
1389
+ createdAt: m.createdAt,
1390
+ updatedAt: m.updatedAt,
1391
+ userId: m.userId,
1392
+ projectId: m.projectId,
1393
+ workspaceId: m.workspaceId,
1394
+ archivedAt: m.archivedAt,
1395
+ durationSeconds: m.durationSeconds ?? null,
1396
+ participantCount: m.participantCount ?? null,
1397
+ videoUrl: m.videoUrl ?? null,
1398
+ project: m.project ?? null,
1399
+ workspace: m.workspace ?? null,
1400
+ sourceIntegration: m.sourceIntegration ?? null,
1401
+ participants: m.participants ?? [],
1402
+ actions: m.actions ?? []
1403
+ };
1404
+ }
1405
+ function transformMeetingListRow(m) {
1406
+ const { notes, summary, transcription, ...rest } = transformMeeting(m);
1407
+ return {
1408
+ ...rest,
1409
+ hasNotes: Boolean(notes),
1410
+ hasSummary: Boolean(summary),
1411
+ hasTranscript: Boolean(transcription)
1412
+ };
1413
+ }
1414
+ function outputMeetingJson(meeting) {
1415
+ console.log(JSON.stringify(transformMeeting(meeting), null, 2));
1416
+ }
1417
+ function outputMeetingPretty(meeting, options = {}) {
1418
+ console.log(chalk3.gray("\u2500".repeat(50)));
1419
+ console.log(`
1420
+ ${chalk3.bold(meeting.title ?? "Untitled meeting")}`);
1421
+ console.log(chalk3.gray(` ID: ${meeting.id}`));
1422
+ console.log(` ${chalk3.gray("Date:")} ${meetingWhen(meeting).toLocaleString()}`);
1423
+ if (meeting.workspace) {
1424
+ console.log(` ${chalk3.gray("Workspace:")} ${meeting.workspace.name}`);
1425
+ }
1426
+ if (meeting.project) {
1427
+ console.log(` ${chalk3.gray("Project:")} ${meeting.project.name}`);
1428
+ }
1429
+ if (meeting.sourceIntegration) {
1430
+ console.log(` ${chalk3.gray("Source:")} ${meeting.sourceIntegration.provider}`);
1431
+ }
1432
+ if (meeting.durationSeconds) {
1433
+ console.log(` ${chalk3.gray("Duration:")} ${Math.round(meeting.durationSeconds / 60)} min`);
1434
+ }
1435
+ if (meeting.archivedAt) {
1436
+ console.log(` ${chalk3.yellow("Archived")}`);
1437
+ }
1438
+ if (meeting.participants && meeting.participants.length > 0) {
1439
+ const people = meeting.participants.map((p) => p.name ?? p.email).join(", ");
1440
+ console.log(` ${chalk3.gray("Participants:")} ${people}`);
1441
+ }
1442
+ if (meeting.description) {
1443
+ console.log(`
1444
+ ${meeting.description}`);
1445
+ }
1446
+ if (meeting.summary) {
1447
+ console.log(`
1448
+ ${chalk3.bold("Summary")}`);
1449
+ console.log(renderMeetingSummary(meeting.summary));
1450
+ }
1451
+ if (meeting.notes) {
1452
+ console.log(`
1453
+ ${chalk3.bold("Notes")}`);
1454
+ console.log(meeting.notes);
1455
+ } else {
1456
+ console.log(chalk3.gray("\nNo notes yet."));
1457
+ }
1458
+ if (options.transcript) {
1459
+ if (meeting.transcription) {
1460
+ console.log(`
1461
+ ${chalk3.bold("Transcript")}`);
1462
+ console.log(meeting.transcription);
1463
+ } else {
1464
+ console.log(chalk3.gray("\nNo transcript."));
1465
+ }
1466
+ } else if (meeting.transcription) {
1467
+ console.log(chalk3.gray("\n(transcript available \u2014 pass --transcript to print it)"));
1468
+ }
1469
+ console.log();
1470
+ }
1471
+ function outputMeetingsJson(meetings) {
1472
+ console.log(
1473
+ JSON.stringify(
1474
+ { meetings: meetings.map(transformMeetingListRow), total: meetings.length },
1475
+ null,
1476
+ 2
1477
+ )
1478
+ );
1479
+ }
1480
+ function outputMeetingsPretty(meetings) {
1481
+ if (meetings.length === 0) {
1482
+ console.log(chalk3.gray("No meetings found."));
1483
+ return;
1484
+ }
1485
+ console.log(chalk3.bold(`
1486
+ Meetings (${meetings.length} total)`));
1487
+ console.log(chalk3.gray("\u2500".repeat(50)));
1488
+ for (const m of meetings) {
1489
+ const flags = [
1490
+ m.notes ? "notes" : null,
1491
+ m.summary ? "summary" : null,
1492
+ m.transcription ? "transcript" : null
1493
+ ].filter(Boolean);
1494
+ const archived = m.archivedAt ? chalk3.yellow(" [archived]") : "";
1495
+ console.log(
1496
+ ` ${chalk3.bold(m.title ?? "Untitled meeting")}${archived} ${chalk3.gray(
1497
+ meetingWhen(m).toLocaleDateString()
1498
+ )}`
1499
+ );
1500
+ console.log(chalk3.gray(` ID: ${m.id}`));
1501
+ const context = [
1502
+ m.project ? `Project: ${m.project.name}` : null,
1503
+ m.participantCount ? `${m.participantCount} participants` : null,
1504
+ flags.length > 0 ? flags.join(", ") : null
1505
+ ].filter(Boolean);
1506
+ if (context.length > 0) {
1507
+ console.log(chalk3.gray(` ${context.join(" \xB7 ")}`));
1508
+ }
1509
+ }
1510
+ console.log();
1511
+ }
1361
1512
  function outputRequirementJson(requirement) {
1362
1513
  console.log(JSON.stringify(requirement, null, 2));
1363
1514
  }
@@ -3804,8 +3955,276 @@ function createLabelsCommand() {
3804
3955
  return labels;
3805
3956
  }
3806
3957
 
3807
- // src/commands/organizations.ts
3958
+ // src/commands/meetings.ts
3808
3959
  import { Command as Command13 } from "commander";
3960
+ import chalk6 from "chalk";
3961
+
3962
+ // src/utils/input.ts
3963
+ import { readFileSync } from "fs";
3964
+ var stdinConsumed = false;
3965
+ function readText(inline, file) {
3966
+ if (inline !== void 0) return inline;
3967
+ if (file === void 0) return void 0;
3968
+ if (file === "-") {
3969
+ if (stdinConsumed) {
3970
+ throw new Error('stdin ("-") can only back one option per invocation.');
3971
+ }
3972
+ stdinConsumed = true;
3973
+ return readFileSync(0, "utf-8");
3974
+ }
3975
+ return readFileSync(file, "utf-8");
3976
+ }
3977
+ function parseDate2(value) {
3978
+ if (value === void 0 || value === "") return void 0;
3979
+ const date = /^\d{4}-\d{2}-\d{2}$/.test(value) ? /* @__PURE__ */ new Date(`${value}T00:00:00`) : new Date(value);
3980
+ if (Number.isNaN(date.getTime())) {
3981
+ throw new Error(
3982
+ `Invalid date: "${value}". Use ISO format, e.g. 2026-08-25 or 2026-08-25T14:00.`
3983
+ );
3984
+ }
3985
+ return date;
3986
+ }
3987
+
3988
+ // src/commands/meetings.ts
3989
+ function createMeetingsCommand() {
3990
+ const meetings = new Command13("meetings").description(
3991
+ "Manage meetings and their notes (recorded, imported, or manually created)"
3992
+ );
3993
+ meetings.command("list").description(
3994
+ 'List meetings you can see (owned, attended, or shared via project/workspace). JSON rows carry hasNotes/hasSummary/hasTranscript flags instead of the bodies \u2014 use "get" for content.'
3995
+ ).option("--workspace <slug|id>", "Restrict to one workspace").option("--mine", "Only meetings you own or attended").option("--archived", "Include archived meetings").action(
3996
+ async (options, cmd) => {
3997
+ const globalOpts = cmd.optsWithGlobals();
3998
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
3999
+ try {
4000
+ const client = getClient();
4001
+ const workspaceId = options.workspace ? await resolveWorkspaceId(client, options.workspace) : void 0;
4002
+ const list = await client.meetings.list({
4003
+ workspaceId,
4004
+ includeArchived: options.archived,
4005
+ meetingType: options.mine ? "mine" : void 0
4006
+ });
4007
+ if (useJson) outputMeetingsJson(list);
4008
+ else outputMeetingsPretty(list);
4009
+ } catch (error) {
4010
+ handleError(error, useJson);
4011
+ }
4012
+ }
4013
+ );
4014
+ meetings.command("get <id>").description("Get a meeting: details, summary, and notes").option("--transcript", "Also print the raw transcript (pretty mode; JSON always includes it)").action(
4015
+ async (id, options, cmd) => {
4016
+ const globalOpts = cmd.optsWithGlobals();
4017
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
4018
+ try {
4019
+ const client = getClient();
4020
+ const meeting = await client.meetings.get(id);
4021
+ if (useJson) outputMeetingJson(meeting);
4022
+ else outputMeetingPretty(meeting, { transcript: options.transcript });
4023
+ } catch (error) {
4024
+ handleError(error, useJson);
4025
+ }
4026
+ }
4027
+ );
4028
+ meetings.command("create").description(
4029
+ "Create a meeting manually. Transcript: --transcript text, --transcript-file <path>, or --transcript-file - for stdin."
4030
+ ).requiredOption("-t, --title <title>", "Meeting title").option("--transcript <text>", "Transcript text (required by the API)").option("--transcript-file <path>", 'Read the transcript from a file ("-" = stdin)').option("--notes <markdown>", "Meeting notes").option("--notes-file <path>", 'Read the notes from a file ("-" = stdin)').option("--description <text>", "Short description").option("--date <iso>", "When the meeting occurred (e.g. 2026-08-25T14:00; a bare date is local midnight)").option("--project <id>", "Link to a project (the meeting inherits its workspace)").option("--workspace <slug|id>", "Workspace (omit for a personal meeting)").action(
4031
+ async (options, cmd) => {
4032
+ const globalOpts = cmd.optsWithGlobals();
4033
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
4034
+ try {
4035
+ const client = getClient();
4036
+ const transcription = readText(options.transcript, options.transcriptFile);
4037
+ if (transcription === void 0) {
4038
+ throw new Error(
4039
+ "A transcript is required: pass --transcript <text> or --transcript-file <path|->."
4040
+ );
4041
+ }
4042
+ if (transcription.trim() === "") {
4043
+ throw new Error("The transcript is empty. The API requires a non-empty transcript.");
4044
+ }
4045
+ const workspaceId = options.workspace ? await resolveWorkspaceId(client, options.workspace) : void 0;
4046
+ const meeting = await client.meetings.create({
4047
+ title: options.title,
4048
+ transcription,
4049
+ notes: readText(options.notes, options.notesFile),
4050
+ description: options.description,
4051
+ meetingDate: parseDate2(options.date),
4052
+ projectId: options.project,
4053
+ workspaceId
4054
+ });
4055
+ if (useJson) outputMeetingJson(meeting);
4056
+ else {
4057
+ console.log("\n\u2713 Meeting created");
4058
+ outputMeetingPretty(meeting);
4059
+ }
4060
+ } catch (error) {
4061
+ handleError(error, useJson);
4062
+ }
4063
+ }
4064
+ );
4065
+ meetings.command("update").description(
4066
+ "Update a meeting; only the fields you pass are written. Title and other fields are two API calls \u2014 on a mid-flight failure the title may already be saved."
4067
+ ).requiredOption("--id <id>", "Meeting CUID").option("-t, --title <title>", "New title").option("--notes <markdown>", "Replace the meeting notes").option("--notes-file <path>", 'Read the new notes from a file ("-" = stdin)').option("--summary <text>", "Replace the summary").option("--description <text>", "Replace the description").option("--date <iso>", "When the meeting occurred").action(
4068
+ async (options, cmd) => {
4069
+ const globalOpts = cmd.optsWithGlobals();
4070
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
4071
+ try {
4072
+ const notes2 = readText(options.notes, options.notesFile);
4073
+ if (options.title === void 0 && notes2 === void 0 && options.summary === void 0 && options.description === void 0 && options.date === void 0) {
4074
+ throw new Error(
4075
+ "Nothing to update. Pass at least one of --title, --notes, --notes-file, --summary, --description, --date."
4076
+ );
4077
+ }
4078
+ const client = getClient();
4079
+ const meeting = await client.meetings.update({
4080
+ id: options.id,
4081
+ title: options.title,
4082
+ notes: notes2,
4083
+ summary: options.summary,
4084
+ description: options.description,
4085
+ meetingDate: parseDate2(options.date)
4086
+ });
4087
+ if (useJson) outputMeetingJson(meeting);
4088
+ else {
4089
+ console.log("\n\u2713 Meeting updated");
4090
+ outputMeetingPretty(meeting);
4091
+ }
4092
+ } catch (error) {
4093
+ handleError(error, useJson);
4094
+ }
4095
+ }
4096
+ );
4097
+ meetings.command("delete [ids...]").description(
4098
+ 'Permanently delete meetings you own. Pass one or more ids, and/or --ids-file with whitespace-separated ids ("-" = stdin). Deleting more than one id requires --force. Exit 0 only when everything requested was deleted.'
4099
+ ).option("--ids-file <path>", 'Read whitespace-separated meeting ids from a file ("-" = stdin)').option("--force", "Confirm deleting more than one meeting in a single call").action(
4100
+ async (idArgs, options, cmd) => {
4101
+ const globalOpts = cmd.optsWithGlobals();
4102
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
4103
+ try {
4104
+ const fromFile = readText(void 0, options.idsFile);
4105
+ const ids = [
4106
+ ...new Set([
4107
+ ...idArgs,
4108
+ ...fromFile ? fromFile.split(/\s+/) : []
4109
+ ].filter((id) => id !== ""))
4110
+ ];
4111
+ if (ids.length === 0) {
4112
+ throw new Error(
4113
+ "Nothing to delete. Pass one or more meeting ids, or --ids-file <path|->."
4114
+ );
4115
+ }
4116
+ const malformed = ids.filter((id) => !/^[a-z][a-z0-9]{15,}$/i.test(id));
4117
+ if (malformed.length > 0) {
4118
+ throw new Error(
4119
+ `These don't look like meeting ids: ${malformed.slice(0, 5).join(", ")}${malformed.length > 5 ? ` (+${malformed.length - 5} more)` : ""}. Nothing was deleted.`
4120
+ );
4121
+ }
4122
+ if (ids.length > 1 && !options.force) {
4123
+ throw new Error(
4124
+ `Refusing to delete ${ids.length} meetings without --force. Deletion is permanent.`
4125
+ );
4126
+ }
4127
+ const client = getClient();
4128
+ let count;
4129
+ if (ids.length === 1) {
4130
+ await client.meetings.delete(ids[0]);
4131
+ count = 1;
4132
+ } else {
4133
+ count = 0;
4134
+ for (let i = 0; i < ids.length; i += 500) {
4135
+ const chunk = ids.slice(i, i + 500);
4136
+ count += (await client.meetings.deleteMany(chunk)).count;
4137
+ }
4138
+ }
4139
+ const skipped = ids.length - count;
4140
+ if (skipped > 0) {
4141
+ process.exitCode = 1;
4142
+ }
4143
+ if (useJson) {
4144
+ console.log(
4145
+ JSON.stringify({ requested: ids.length, count, skipped }, null, 2)
4146
+ );
4147
+ } else {
4148
+ console.log(`\u2713 Deleted ${count} of ${ids.length} meeting${ids.length === 1 ? "" : "s"}`);
4149
+ if (skipped > 0) {
4150
+ console.log(
4151
+ chalk6.yellow(
4152
+ `${skipped} id(s) were skipped \u2014 not found, or not owned by you.`
4153
+ )
4154
+ );
4155
+ }
4156
+ }
4157
+ } catch (error) {
4158
+ handleError(error, useJson);
4159
+ }
4160
+ }
4161
+ );
4162
+ const notes = new Command13("notes").description(
4163
+ "Read and write a meeting's notes"
4164
+ );
4165
+ notes.command("get <id>").description(
4166
+ "Print the meeting notes as raw Markdown (safe to redirect to a file); pass --json for a {id, notes} envelope"
4167
+ ).action(async (id, _options, cmd) => {
4168
+ const globalOpts = cmd.optsWithGlobals();
4169
+ try {
4170
+ const client = getClient();
4171
+ const body = await client.meetings.getNotes(id);
4172
+ if (globalOpts.json) {
4173
+ console.log(JSON.stringify({ id, notes: body }, null, 2));
4174
+ } else if (body !== null) {
4175
+ process.stdout.write(body.endsWith("\n") ? body : `${body}
4176
+ `);
4177
+ } else if (process.stdout.isTTY) {
4178
+ console.log(chalk6.gray("No notes yet."));
4179
+ }
4180
+ } catch (error) {
4181
+ handleError(error, shouldUseJson(globalOpts.json, globalOpts.pretty));
4182
+ }
4183
+ });
4184
+ notes.command("set <id> [notes]").description("Replace the meeting notes. Body: inline, --file <path>, or --file - for stdin.").option("--file <path>", 'Read the notes from a file ("-" = stdin)').action(
4185
+ async (id, inline, options, cmd) => {
4186
+ const globalOpts = cmd.optsWithGlobals();
4187
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
4188
+ try {
4189
+ const body = readText(inline, options.file);
4190
+ if (body === void 0) {
4191
+ throw new Error("Pass the notes inline or via --file <path|->.");
4192
+ }
4193
+ const client = getClient();
4194
+ const meeting = await client.meetings.setNotes(id, body);
4195
+ if (useJson) outputMeetingJson(meeting);
4196
+ else console.log("\u2713 Notes saved");
4197
+ } catch (error) {
4198
+ handleError(error, useJson);
4199
+ }
4200
+ }
4201
+ );
4202
+ notes.command("append <id> [notes]").description(
4203
+ "Append a block to the meeting notes (separated by a blank line). Read-modify-write: concurrent appends can lose one side."
4204
+ ).option("--file <path>", 'Read the block from a file ("-" = stdin)').action(
4205
+ async (id, inline, options, cmd) => {
4206
+ const globalOpts = cmd.optsWithGlobals();
4207
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
4208
+ try {
4209
+ const body = readText(inline, options.file);
4210
+ if (body === void 0) {
4211
+ throw new Error("Pass the notes inline or via --file <path|->.");
4212
+ }
4213
+ const client = getClient();
4214
+ const meeting = await client.meetings.appendNotes(id, body);
4215
+ if (useJson) outputMeetingJson(meeting);
4216
+ else console.log("\u2713 Notes appended");
4217
+ } catch (error) {
4218
+ handleError(error, useJson);
4219
+ }
4220
+ }
4221
+ );
4222
+ meetings.addCommand(notes);
4223
+ return meetings;
4224
+ }
4225
+
4226
+ // src/commands/organizations.ts
4227
+ import { Command as Command14 } from "commander";
3809
4228
  var VALID_SIZES = [
3810
4229
  "1-10",
3811
4230
  "11-50",
@@ -3815,7 +4234,7 @@ var VALID_SIZES = [
3815
4234
  "1000+"
3816
4235
  ];
3817
4236
  function createOrganizationsCommand() {
3818
- const organizations = new Command13("organizations").alias("orgs").description("Manage CRM organizations");
4237
+ const organizations = new Command14("organizations").alias("orgs").description("Manage CRM organizations");
3819
4238
  organizations.command("list").description("List organizations").requiredOption("--workspace <id>", "Workspace ID").option("--search <query>", "Search by name or description").option("--industry <industry>", "Filter by industry").option("--limit <n>", "Max results to return", parseInt).option("--cursor <cursor>", "Pagination cursor").action(async (options, cmd) => {
3820
4239
  const globalOpts = cmd.optsWithGlobals();
3821
4240
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -3883,13 +4302,12 @@ function createOrganizationsCommand() {
3883
4302
  }
3884
4303
 
3885
4304
  // src/commands/pages.ts
3886
- import { readFileSync } from "fs";
3887
- import { Command as Command15 } from "commander";
4305
+ import { Command as Command16 } from "commander";
3888
4306
 
3889
4307
  // src/commands/pageComments.ts
3890
- import { Command as Command14 } from "commander";
4308
+ import { Command as Command15 } from "commander";
3891
4309
  function createPageCommentsCommand() {
3892
- const comment = new Command14("comment").description(
4310
+ const comment = new Command15("comment").description(
3893
4311
  "Read and post comments on a page. Mention teammates with --mention."
3894
4312
  );
3895
4313
  comment.command("list").description("List comments on a page").requiredOption("--page <id>", "Page ID").action(async (options, cmd) => {
@@ -3983,14 +4401,8 @@ function createPageCommentsCommand() {
3983
4401
  }
3984
4402
 
3985
4403
  // src/commands/pages.ts
3986
- function readBody(body, bodyFile) {
3987
- if (body !== void 0) return body;
3988
- if (bodyFile === void 0) return void 0;
3989
- if (bodyFile === "-") return readFileSync(0, "utf-8");
3990
- return readFileSync(bodyFile, "utf-8");
3991
- }
3992
4404
  function createPagesCommand() {
3993
- const pages = new Command15("pages").description(
4405
+ const pages = new Command16("pages").description(
3994
4406
  'Manage Knowledge pages (PRDs, research, technical specs). Link one to a feature with "features link-page".'
3995
4407
  );
3996
4408
  pages.command("list").description("List pages in a workspace").option("--workspace <slug|id>", "Workspace (defaults to the configured default)").option("--search <text>", "Filter by title/content").action(
@@ -4033,7 +4445,7 @@ function createPagesCommand() {
4033
4445
  const page = await client.pages.create({
4034
4446
  workspaceId,
4035
4447
  title: options.title,
4036
- body: readBody(options.body, options.bodyFile)
4448
+ body: readText(options.body, options.bodyFile)
4037
4449
  });
4038
4450
  if (useJson) outputPageJson(page);
4039
4451
  else {
@@ -4056,7 +4468,7 @@ function createPagesCommand() {
4056
4468
  const page = await client.pages.update({
4057
4469
  id: options.id,
4058
4470
  title: options.title,
4059
- body: readBody(options.body, options.bodyFile)
4471
+ body: readText(options.body, options.bodyFile)
4060
4472
  });
4061
4473
  if (useJson) outputPageJson(page);
4062
4474
  else {
@@ -4073,9 +4485,9 @@ function createPagesCommand() {
4073
4485
  }
4074
4486
 
4075
4487
  // src/commands/products.ts
4076
- import { Command as Command16 } from "commander";
4488
+ import { Command as Command17 } from "commander";
4077
4489
  function createProductsCommand() {
4078
- const products = new Command16("products").description(
4490
+ const products = new Command17("products").description(
4079
4491
  "Manage products. A workspace contains products; a product holds features and tickets."
4080
4492
  );
4081
4493
  products.command("list").description("List products in a workspace").option("--workspace <slug|id>", "Workspace slug or CUID (defaults to configured workspace)").action(async (options, cmd) => {
@@ -4138,7 +4550,7 @@ function createProductsCommand() {
4138
4550
  }
4139
4551
 
4140
4552
  // src/commands/projects.ts
4141
- import { Command as Command17 } from "commander";
4553
+ import { Command as Command18 } from "commander";
4142
4554
  var PROJECT_STATUSES = [
4143
4555
  "ACTIVE",
4144
4556
  "ON_HOLD",
@@ -4165,7 +4577,7 @@ function validateProjectPriority(value) {
4165
4577
  return value;
4166
4578
  }
4167
4579
  function createProjectsCommand() {
4168
- const projects = new Command17("projects").description("Manage projects");
4580
+ const projects = new Command18("projects").description("Manage projects");
4169
4581
  projects.command("list").description("List all projects").option("--workspace <slug|id>", "Workspace slug or CUID").option("--include-actions", "Include actions in output").action(async (options, cmd) => {
4170
4582
  const globalOpts = cmd.optsWithGlobals();
4171
4583
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -4284,8 +4696,8 @@ Re-run with --force to delete it anyway.`
4284
4696
  }
4285
4697
 
4286
4698
  // src/commands/search.ts
4287
- import { Command as Command18 } from "commander";
4288
- import chalk6 from "chalk";
4699
+ import { Command as Command19 } from "commander";
4700
+ import chalk7 from "chalk";
4289
4701
  var TYPE_LABELS = {
4290
4702
  workspace: "Workspaces",
4291
4703
  project: "Projects",
@@ -4303,7 +4715,7 @@ var TYPE_LABELS = {
4303
4715
  product: "Products"
4304
4716
  };
4305
4717
  function createSearchCommand() {
4306
- const search = new Command18("search").description(
4718
+ const search = new Command19("search").description(
4307
4719
  "Global search across projects, actions, goals, workspaces and more \u2014 same coverage as the app's Cmd+K palette"
4308
4720
  ).argument("<query>", "Text to search for").option("-w, --workspace <slug>", "Restrict results to one workspace (slug or ID)").option("-l, --limit <n>", "Max results per entity type (1-25, default 10)").action(async (query, options, cmd) => {
4309
4721
  const globalOpts = cmd.optsWithGlobals();
@@ -4317,10 +4729,10 @@ function createSearchCommand() {
4317
4729
  (w) => w.slug === options.workspace || w.id === options.workspace
4318
4730
  );
4319
4731
  if (!match) {
4320
- console.error(chalk6.red(`Workspace "${options.workspace}" not found.`));
4321
- console.log(chalk6.gray("Available workspaces:"));
4732
+ console.error(chalk7.red(`Workspace "${options.workspace}" not found.`));
4733
+ console.log(chalk7.gray("Available workspaces:"));
4322
4734
  workspaces.forEach((w) => {
4323
- console.log(chalk6.gray(` - ${w.slug} (${w.name})`));
4735
+ console.log(chalk7.gray(` - ${w.slug} (${w.name})`));
4324
4736
  });
4325
4737
  process.exit(1);
4326
4738
  }
@@ -4333,7 +4745,7 @@ function createSearchCommand() {
4333
4745
  return;
4334
4746
  }
4335
4747
  if (result.results.length === 0) {
4336
- console.log(chalk6.gray(`No matches for "${result.query}".`));
4748
+ console.log(chalk7.gray(`No matches for "${result.query}".`));
4337
4749
  return;
4338
4750
  }
4339
4751
  const byType = /* @__PURE__ */ new Map();
@@ -4343,14 +4755,14 @@ function createSearchCommand() {
4343
4755
  byType.set(r.type, bucket);
4344
4756
  }
4345
4757
  for (const [type, rows] of byType) {
4346
- console.log(chalk6.bold(`
4758
+ console.log(chalk7.bold(`
4347
4759
  ${TYPE_LABELS[type] ?? type}`));
4348
4760
  for (const row of rows) {
4349
- const parts = [chalk6.white(row.title)];
4350
- if (row.subtitle) parts.push(chalk6.gray(row.subtitle));
4351
- if (row.workspace) parts.push(chalk6.cyan(`@${row.workspace.slug}`));
4352
- console.log(` ${parts.join(chalk6.gray(" \xB7 "))}`);
4353
- console.log(chalk6.gray(` ${row.id}${row.url ? ` ${row.url}` : ""}`));
4761
+ const parts = [chalk7.white(row.title)];
4762
+ if (row.subtitle) parts.push(chalk7.gray(row.subtitle));
4763
+ if (row.workspace) parts.push(chalk7.cyan(`@${row.workspace.slug}`));
4764
+ console.log(` ${parts.join(chalk7.gray(" \xB7 "))}`);
4765
+ console.log(chalk7.gray(` ${row.id}${row.url ? ` ${row.url}` : ""}`));
4354
4766
  }
4355
4767
  }
4356
4768
  console.log();
@@ -4362,7 +4774,7 @@ ${TYPE_LABELS[type] ?? type}`));
4362
4774
  }
4363
4775
 
4364
4776
  // src/commands/tickets.ts
4365
- import { Command as Command19 } from "commander";
4777
+ import { Command as Command20 } from "commander";
4366
4778
  function collectRepeatable(value, previous) {
4367
4779
  return [...previous, value];
4368
4780
  }
@@ -4405,7 +4817,7 @@ function validateTicketType(value) {
4405
4817
  return value;
4406
4818
  }
4407
4819
  function createTicketsCommand() {
4408
- const tickets = new Command19("tickets").description(
4820
+ const tickets = new Command20("tickets").description(
4409
4821
  "Manage product backlog tickets. Tickets live under a product, link to features/epics, depend on each other, and can be implemented via actions."
4410
4822
  );
4411
4823
  tickets.command("list").description(
@@ -4518,7 +4930,7 @@ function createTicketsCommand() {
4518
4930
  handleError(error, useJson);
4519
4931
  }
4520
4932
  });
4521
- tickets.command("create").description("Create a new ticket in a product").requiredOption("--product <slug|id>", "Product slug or CUID").requiredOption("-t, --title <title>", "Ticket title").option("-b, --body <text>", "Body (markdown supported)").option("--type <type>", `Ticket type: ${TICKET_TYPES.join(", ")}`).option("--status <status>", `Initial status: ${TICKET_STATUSES.join(", ")}`).option("--priority <n>", "Priority 0-4 (lower = higher)", parseInt).option("--points <n>", "Story points", parseFloat).option("--feature <id>", "Feature CUID to attach to").option("--scope <id>", "Feature scope CUID to attach to (requires --feature)").option("--epic <id>", "Epic CUID to attach to").option("--assignee <id>", "Assignee user ID").option("--branch <name>", "Branch name").option("--pr <url>", "Pull request URL").option("--workspace <slug|id>", "Workspace (required when --product is a slug)").option(
4933
+ tickets.command("create").description("Create a new ticket in a product").requiredOption("--product <slug|id>", "Product slug or CUID").requiredOption("-t, --title <title>", "Ticket title").option("-b, --body <text>", "Body (markdown supported)").option("--type <type>", `Ticket type: ${TICKET_TYPES.join(", ")}`).option("--status <status>", `Initial status: ${TICKET_STATUSES.join(", ")}`).option("--priority <n>", "Priority 0-4 (lower = higher)", parseInt).option("--points <n>", "Story points", parseFloat).option("--feature <id>", "Feature CUID to attach to").option("--scope <id>", "Feature scope CUID to attach to (requires --feature)").option("--epic <id>", "Epic CUID to attach to").option("--cycle <id>", "Cycle CUID to assign to").option("--assignee <id>", "Assignee user ID").option("--branch <name>", "Branch name").option("--pr <url>", "Pull request URL").option("--workspace <slug|id>", "Workspace (required when --product is a slug)").option(
4522
4934
  "--label <slug-or-id>",
4523
4935
  "Label to attach (repeatable)",
4524
4936
  collectRepeatable,
@@ -4548,6 +4960,7 @@ function createTicketsCommand() {
4548
4960
  featureId: options.feature,
4549
4961
  scopeId: options.scope,
4550
4962
  epicId: options.epic,
4963
+ cycleId: options.cycle,
4551
4964
  assigneeId: options.assignee,
4552
4965
  branchName: options.branch,
4553
4966
  prUrl: options.pr
@@ -4570,7 +4983,7 @@ function createTicketsCommand() {
4570
4983
  }
4571
4984
  }
4572
4985
  );
4573
- tickets.command("update").description("Update an existing ticket").requiredOption("--id <id>", "Ticket CUID").option("-t, --title <title>", "New title").option("-b, --body <text>", "New body").option("--type <type>", `Type: ${TICKET_TYPES.join(", ")}`).option("--status <status>", `Status: ${TICKET_STATUSES.join(", ")}`).option("--priority <n>", 'Priority 0-4 (or "null" to clear)').option("--points <n>", 'Story points (or "null" to clear)').option("--feature <id>", 'Feature CUID (or "null" to detach)').option("--epic <id>", 'Epic CUID (or "null" to detach)').option("--assignee <id>", 'Assignee user ID (or "null" to unassign)').option("--branch <name>", 'Branch name (or "null")').option("--pr <url>", 'PR URL (or "null")').option(
4986
+ tickets.command("update").description("Update an existing ticket").requiredOption("--id <id>", "Ticket CUID").option("-t, --title <title>", "New title").option("-b, --body <text>", "New body").option("--type <type>", `Type: ${TICKET_TYPES.join(", ")}`).option("--status <status>", `Status: ${TICKET_STATUSES.join(", ")}`).option("--priority <n>", 'Priority 0-4 (or "null" to clear)').option("--points <n>", 'Story points (or "null" to clear)').option("--feature <id>", 'Feature CUID (or "null" to detach)').option("--epic <id>", 'Epic CUID (or "null" to detach)').option("--cycle <id>", 'Cycle CUID (or "null" to detach)').option("--assignee <id>", 'Assignee user ID (or "null" to unassign)').option("--branch <name>", 'Branch name (or "null")').option("--pr <url>", 'PR URL (or "null")').option(
4574
4987
  "--add-label <slug-or-id>",
4575
4988
  "Label to attach to this ticket (repeatable)",
4576
4989
  collectRepeatable,
@@ -4617,6 +5030,7 @@ function createTicketsCommand() {
4617
5030
  points: parseNullableFloat(options.points),
4618
5031
  featureId: parseNullableString(options.feature),
4619
5032
  epicId: parseNullableString(options.epic),
5033
+ cycleId: parseNullableString(options.cycle),
4620
5034
  assigneeId: parseNullableString(options.assignee),
4621
5035
  branchName: parseNullableString(options.branch),
4622
5036
  prUrl: parseNullableString(options.pr)
@@ -4729,7 +5143,7 @@ function createTicketsCommand() {
4729
5143
  handleError(error, useJson);
4730
5144
  }
4731
5145
  });
4732
- const comment = new Command19("comment").description("Manage comments on a ticket");
5146
+ const comment = new Command20("comment").description("Manage comments on a ticket");
4733
5147
  comment.command("list").description("List comments on a ticket").requiredOption("--id <id>", "Ticket CUID").action(async (options, cmd) => {
4734
5148
  const globalOpts = cmd.optsWithGlobals();
4735
5149
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -4821,10 +5235,10 @@ function createTicketsCommand() {
4821
5235
  }
4822
5236
 
4823
5237
  // src/commands/workspaces.ts
4824
- import { Command as Command20 } from "commander";
4825
- import chalk7 from "chalk";
5238
+ import { Command as Command21 } from "commander";
5239
+ import chalk8 from "chalk";
4826
5240
  function createWorkspacesCommand() {
4827
- const workspaces = new Command20("workspaces").description("Manage workspaces");
5241
+ const workspaces = new Command21("workspaces").description("Manage workspaces");
4828
5242
  workspaces.command("list").description("List all workspaces").action(async (_options, cmd) => {
4829
5243
  const globalOpts = cmd.optsWithGlobals();
4830
5244
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -4837,7 +5251,7 @@ function createWorkspacesCommand() {
4837
5251
  const config = getConfig();
4838
5252
  outputWorkspacesPretty(workspaces2);
4839
5253
  if (config.defaultWorkspaceSlug) {
4840
- console.log(chalk7.gray(`Default workspace: ${config.defaultWorkspaceSlug}`));
5254
+ console.log(chalk8.gray(`Default workspace: ${config.defaultWorkspaceSlug}`));
4841
5255
  }
4842
5256
  }
4843
5257
  } catch (error) {
@@ -4850,10 +5264,10 @@ function createWorkspacesCommand() {
4850
5264
  const workspaces2 = await client.workspaces.list();
4851
5265
  const workspace = workspaces2.find((w) => w.slug === slug);
4852
5266
  if (!workspace) {
4853
- console.error(chalk7.red(`Workspace with slug "${slug}" not found.`));
4854
- console.log(chalk7.gray("Available workspaces:"));
5267
+ console.error(chalk8.red(`Workspace with slug "${slug}" not found.`));
5268
+ console.log(chalk8.gray("Available workspaces:"));
4855
5269
  workspaces2.forEach((w) => {
4856
- console.log(chalk7.gray(` - ${w.slug} (${w.name})`));
5270
+ console.log(chalk8.gray(` - ${w.slug} (${w.name})`));
4857
5271
  });
4858
5272
  process.exit(1);
4859
5273
  }
@@ -4861,7 +5275,7 @@ function createWorkspacesCommand() {
4861
5275
  defaultWorkspaceId: workspace.id,
4862
5276
  defaultWorkspaceSlug: workspace.slug
4863
5277
  });
4864
- console.log(chalk7.green(`Default workspace set to: ${workspace.name} (${workspace.slug})`));
5278
+ console.log(chalk8.green(`Default workspace set to: ${workspace.name} (${workspace.slug})`));
4865
5279
  } catch (error) {
4866
5280
  handleError(error);
4867
5281
  }
@@ -4889,20 +5303,20 @@ function createWorkspacesCommand() {
4889
5303
  return;
4890
5304
  }
4891
5305
  if (members.length === 0) {
4892
- console.log(chalk7.gray("No members found."));
5306
+ console.log(chalk8.gray("No members found."));
4893
5307
  return;
4894
5308
  }
4895
- console.log(chalk7.bold(`
5309
+ console.log(chalk8.bold(`
4896
5310
  Members (${members.length})
4897
5311
  `));
4898
5312
  for (const m of members) {
4899
5313
  const label = m.name ?? m.email ?? m.id;
4900
- const via = m.source === "team" ? chalk7.gray(
5314
+ const via = m.source === "team" ? chalk8.gray(
4901
5315
  ` via team${m.teams.length ? `: ${m.teams.map((t) => t.name).join(", ")}` : ""}` + (m.teamRole ? ` [team ${m.teamRole}]` : "")
4902
5316
  ) : "";
4903
- console.log(`${chalk7.bold(label)} ${chalk7.gray(`(${m.role})`)}${via}`);
4904
- if (m.email) console.log(chalk7.gray(` ${m.email}`));
4905
- console.log(chalk7.gray(` mention: ${m.mentionSyntax}`));
5317
+ console.log(`${chalk8.bold(label)} ${chalk8.gray(`(${m.role})`)}${via}`);
5318
+ if (m.email) console.log(chalk8.gray(` ${m.email}`));
5319
+ console.log(chalk8.gray(` mention: ${m.mentionSyntax}`));
4906
5320
  console.log();
4907
5321
  }
4908
5322
  } catch (error) {
@@ -4914,7 +5328,7 @@ Members (${members.length})
4914
5328
  }
4915
5329
 
4916
5330
  // src/commands/goals.ts
4917
- import { Command as Command21 } from "commander";
5331
+ import { Command as Command22 } from "commander";
4918
5332
  var GOAL_STATUSES = [
4919
5333
  "planned",
4920
5334
  "active",
@@ -5026,7 +5440,7 @@ function validateKeyResultUnit(value) {
5026
5440
  return value;
5027
5441
  }
5028
5442
  function createKeyResultsCommand() {
5029
- const kr = new Command21("kr").description(
5443
+ const kr = new Command22("kr").description(
5030
5444
  "Key results on an objective. Ids are CUIDs; --goal takes the objective's integer id."
5031
5445
  );
5032
5446
  kr.command("list").description("List key results").option("--goal <id>", "Objective ID (a number)").option("-w, --workspace <slug|id>", "Workspace (workspace-wide: every member's KRs)").option("--period <period>", "Period, e.g. Q3-2026").option("--status <status>", `Filter by status: ${KEY_RESULT_STATUSES.join(", ")}`).option("--mine", "Narrow a workspace list to key results you own").action(
@@ -5229,7 +5643,7 @@ function createKeyResultsCommand() {
5229
5643
  return kr;
5230
5644
  }
5231
5645
  function createGoalCommentsCommand() {
5232
- const comment = new Command21("comment").description(
5646
+ const comment = new Command22("comment").description(
5233
5647
  "Read and post comments on a goal. Mention teammates with --mention."
5234
5648
  );
5235
5649
  comment.command("list").description("List comments on a goal").requiredOption("--goal <id>", "Goal ID (a number)").action(async (options, cmd) => {
@@ -5324,7 +5738,7 @@ function createGoalCommentsCommand() {
5324
5738
  return comment;
5325
5739
  }
5326
5740
  function createGoalsCommand() {
5327
- const goals = new Command21("goals").description(
5741
+ const goals = new Command22("goals").description(
5328
5742
  "Manage goals (objectives) and their key results. Objectives have integer ids; key results are CUIDs."
5329
5743
  );
5330
5744
  goals.command("list").description(
@@ -5590,7 +6004,7 @@ Re-run with --with-key-results to go ahead, or move them with \`goals kr update
5590
6004
  return goals;
5591
6005
  }
5592
6006
  function createOkrsCommand() {
5593
- const okrs = new Command21("okrs").description(
6007
+ const okrs = new Command22("okrs").description(
5594
6008
  "OKRs \u2014 objectives with their key results nested. Manage the parts with `goals` and `goals kr`."
5595
6009
  );
5596
6010
  okrs.command("list").description(
@@ -5674,7 +6088,7 @@ import { fileURLToPath } from "url";
5674
6088
  var PKG_VERSION = JSON.parse(
5675
6089
  readFileSync2(join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf-8")
5676
6090
  ).version;
5677
- var program = new Command22();
6091
+ var program = new Command23();
5678
6092
  program.name("exponential").description(
5679
6093
  [
5680
6094
  "CLI to interact with Exponential productivity app.",
@@ -5691,6 +6105,7 @@ program.addCommand(createDealsCommand());
5691
6105
  program.addCommand(createEpicsCommand());
5692
6106
  program.addCommand(createFeaturesCommand());
5693
6107
  program.addCommand(createLabelsCommand());
6108
+ program.addCommand(createMeetingsCommand());
5694
6109
  program.addCommand(createOrganizationsCommand());
5695
6110
  program.addCommand(createPagesCommand());
5696
6111
  program.addCommand(createProductsCommand());