exponential-cli 1.12.0 → 1.13.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,211 @@ 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
+ const notes = new Command13("notes").description(
4098
+ "Read and write a meeting's notes"
4099
+ );
4100
+ notes.command("get <id>").description(
4101
+ "Print the meeting notes as raw Markdown (safe to redirect to a file); pass --json for a {id, notes} envelope"
4102
+ ).action(async (id, _options, cmd) => {
4103
+ const globalOpts = cmd.optsWithGlobals();
4104
+ try {
4105
+ const client = getClient();
4106
+ const body = await client.meetings.getNotes(id);
4107
+ if (globalOpts.json) {
4108
+ console.log(JSON.stringify({ id, notes: body }, null, 2));
4109
+ } else if (body !== null) {
4110
+ process.stdout.write(body.endsWith("\n") ? body : `${body}
4111
+ `);
4112
+ } else if (process.stdout.isTTY) {
4113
+ console.log(chalk6.gray("No notes yet."));
4114
+ }
4115
+ } catch (error) {
4116
+ handleError(error, shouldUseJson(globalOpts.json, globalOpts.pretty));
4117
+ }
4118
+ });
4119
+ 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(
4120
+ async (id, inline, options, cmd) => {
4121
+ const globalOpts = cmd.optsWithGlobals();
4122
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
4123
+ try {
4124
+ const body = readText(inline, options.file);
4125
+ if (body === void 0) {
4126
+ throw new Error("Pass the notes inline or via --file <path|->.");
4127
+ }
4128
+ const client = getClient();
4129
+ const meeting = await client.meetings.setNotes(id, body);
4130
+ if (useJson) outputMeetingJson(meeting);
4131
+ else console.log("\u2713 Notes saved");
4132
+ } catch (error) {
4133
+ handleError(error, useJson);
4134
+ }
4135
+ }
4136
+ );
4137
+ notes.command("append <id> [notes]").description(
4138
+ "Append a block to the meeting notes (separated by a blank line). Read-modify-write: concurrent appends can lose one side."
4139
+ ).option("--file <path>", 'Read the block from a file ("-" = stdin)').action(
4140
+ async (id, inline, options, cmd) => {
4141
+ const globalOpts = cmd.optsWithGlobals();
4142
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
4143
+ try {
4144
+ const body = readText(inline, options.file);
4145
+ if (body === void 0) {
4146
+ throw new Error("Pass the notes inline or via --file <path|->.");
4147
+ }
4148
+ const client = getClient();
4149
+ const meeting = await client.meetings.appendNotes(id, body);
4150
+ if (useJson) outputMeetingJson(meeting);
4151
+ else console.log("\u2713 Notes appended");
4152
+ } catch (error) {
4153
+ handleError(error, useJson);
4154
+ }
4155
+ }
4156
+ );
4157
+ meetings.addCommand(notes);
4158
+ return meetings;
4159
+ }
4160
+
4161
+ // src/commands/organizations.ts
4162
+ import { Command as Command14 } from "commander";
3809
4163
  var VALID_SIZES = [
3810
4164
  "1-10",
3811
4165
  "11-50",
@@ -3815,7 +4169,7 @@ var VALID_SIZES = [
3815
4169
  "1000+"
3816
4170
  ];
3817
4171
  function createOrganizationsCommand() {
3818
- const organizations = new Command13("organizations").alias("orgs").description("Manage CRM organizations");
4172
+ const organizations = new Command14("organizations").alias("orgs").description("Manage CRM organizations");
3819
4173
  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
4174
  const globalOpts = cmd.optsWithGlobals();
3821
4175
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -3883,13 +4237,12 @@ function createOrganizationsCommand() {
3883
4237
  }
3884
4238
 
3885
4239
  // src/commands/pages.ts
3886
- import { readFileSync } from "fs";
3887
- import { Command as Command15 } from "commander";
4240
+ import { Command as Command16 } from "commander";
3888
4241
 
3889
4242
  // src/commands/pageComments.ts
3890
- import { Command as Command14 } from "commander";
4243
+ import { Command as Command15 } from "commander";
3891
4244
  function createPageCommentsCommand() {
3892
- const comment = new Command14("comment").description(
4245
+ const comment = new Command15("comment").description(
3893
4246
  "Read and post comments on a page. Mention teammates with --mention."
3894
4247
  );
3895
4248
  comment.command("list").description("List comments on a page").requiredOption("--page <id>", "Page ID").action(async (options, cmd) => {
@@ -3983,14 +4336,8 @@ function createPageCommentsCommand() {
3983
4336
  }
3984
4337
 
3985
4338
  // 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
4339
  function createPagesCommand() {
3993
- const pages = new Command15("pages").description(
4340
+ const pages = new Command16("pages").description(
3994
4341
  'Manage Knowledge pages (PRDs, research, technical specs). Link one to a feature with "features link-page".'
3995
4342
  );
3996
4343
  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 +4380,7 @@ function createPagesCommand() {
4033
4380
  const page = await client.pages.create({
4034
4381
  workspaceId,
4035
4382
  title: options.title,
4036
- body: readBody(options.body, options.bodyFile)
4383
+ body: readText(options.body, options.bodyFile)
4037
4384
  });
4038
4385
  if (useJson) outputPageJson(page);
4039
4386
  else {
@@ -4056,7 +4403,7 @@ function createPagesCommand() {
4056
4403
  const page = await client.pages.update({
4057
4404
  id: options.id,
4058
4405
  title: options.title,
4059
- body: readBody(options.body, options.bodyFile)
4406
+ body: readText(options.body, options.bodyFile)
4060
4407
  });
4061
4408
  if (useJson) outputPageJson(page);
4062
4409
  else {
@@ -4073,9 +4420,9 @@ function createPagesCommand() {
4073
4420
  }
4074
4421
 
4075
4422
  // src/commands/products.ts
4076
- import { Command as Command16 } from "commander";
4423
+ import { Command as Command17 } from "commander";
4077
4424
  function createProductsCommand() {
4078
- const products = new Command16("products").description(
4425
+ const products = new Command17("products").description(
4079
4426
  "Manage products. A workspace contains products; a product holds features and tickets."
4080
4427
  );
4081
4428
  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 +4485,7 @@ function createProductsCommand() {
4138
4485
  }
4139
4486
 
4140
4487
  // src/commands/projects.ts
4141
- import { Command as Command17 } from "commander";
4488
+ import { Command as Command18 } from "commander";
4142
4489
  var PROJECT_STATUSES = [
4143
4490
  "ACTIVE",
4144
4491
  "ON_HOLD",
@@ -4165,7 +4512,7 @@ function validateProjectPriority(value) {
4165
4512
  return value;
4166
4513
  }
4167
4514
  function createProjectsCommand() {
4168
- const projects = new Command17("projects").description("Manage projects");
4515
+ const projects = new Command18("projects").description("Manage projects");
4169
4516
  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
4517
  const globalOpts = cmd.optsWithGlobals();
4171
4518
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -4284,8 +4631,8 @@ Re-run with --force to delete it anyway.`
4284
4631
  }
4285
4632
 
4286
4633
  // src/commands/search.ts
4287
- import { Command as Command18 } from "commander";
4288
- import chalk6 from "chalk";
4634
+ import { Command as Command19 } from "commander";
4635
+ import chalk7 from "chalk";
4289
4636
  var TYPE_LABELS = {
4290
4637
  workspace: "Workspaces",
4291
4638
  project: "Projects",
@@ -4303,7 +4650,7 @@ var TYPE_LABELS = {
4303
4650
  product: "Products"
4304
4651
  };
4305
4652
  function createSearchCommand() {
4306
- const search = new Command18("search").description(
4653
+ const search = new Command19("search").description(
4307
4654
  "Global search across projects, actions, goals, workspaces and more \u2014 same coverage as the app's Cmd+K palette"
4308
4655
  ).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
4656
  const globalOpts = cmd.optsWithGlobals();
@@ -4317,10 +4664,10 @@ function createSearchCommand() {
4317
4664
  (w) => w.slug === options.workspace || w.id === options.workspace
4318
4665
  );
4319
4666
  if (!match) {
4320
- console.error(chalk6.red(`Workspace "${options.workspace}" not found.`));
4321
- console.log(chalk6.gray("Available workspaces:"));
4667
+ console.error(chalk7.red(`Workspace "${options.workspace}" not found.`));
4668
+ console.log(chalk7.gray("Available workspaces:"));
4322
4669
  workspaces.forEach((w) => {
4323
- console.log(chalk6.gray(` - ${w.slug} (${w.name})`));
4670
+ console.log(chalk7.gray(` - ${w.slug} (${w.name})`));
4324
4671
  });
4325
4672
  process.exit(1);
4326
4673
  }
@@ -4333,7 +4680,7 @@ function createSearchCommand() {
4333
4680
  return;
4334
4681
  }
4335
4682
  if (result.results.length === 0) {
4336
- console.log(chalk6.gray(`No matches for "${result.query}".`));
4683
+ console.log(chalk7.gray(`No matches for "${result.query}".`));
4337
4684
  return;
4338
4685
  }
4339
4686
  const byType = /* @__PURE__ */ new Map();
@@ -4343,14 +4690,14 @@ function createSearchCommand() {
4343
4690
  byType.set(r.type, bucket);
4344
4691
  }
4345
4692
  for (const [type, rows] of byType) {
4346
- console.log(chalk6.bold(`
4693
+ console.log(chalk7.bold(`
4347
4694
  ${TYPE_LABELS[type] ?? type}`));
4348
4695
  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}` : ""}`));
4696
+ const parts = [chalk7.white(row.title)];
4697
+ if (row.subtitle) parts.push(chalk7.gray(row.subtitle));
4698
+ if (row.workspace) parts.push(chalk7.cyan(`@${row.workspace.slug}`));
4699
+ console.log(` ${parts.join(chalk7.gray(" \xB7 "))}`);
4700
+ console.log(chalk7.gray(` ${row.id}${row.url ? ` ${row.url}` : ""}`));
4354
4701
  }
4355
4702
  }
4356
4703
  console.log();
@@ -4362,7 +4709,7 @@ ${TYPE_LABELS[type] ?? type}`));
4362
4709
  }
4363
4710
 
4364
4711
  // src/commands/tickets.ts
4365
- import { Command as Command19 } from "commander";
4712
+ import { Command as Command20 } from "commander";
4366
4713
  function collectRepeatable(value, previous) {
4367
4714
  return [...previous, value];
4368
4715
  }
@@ -4405,7 +4752,7 @@ function validateTicketType(value) {
4405
4752
  return value;
4406
4753
  }
4407
4754
  function createTicketsCommand() {
4408
- const tickets = new Command19("tickets").description(
4755
+ const tickets = new Command20("tickets").description(
4409
4756
  "Manage product backlog tickets. Tickets live under a product, link to features/epics, depend on each other, and can be implemented via actions."
4410
4757
  );
4411
4758
  tickets.command("list").description(
@@ -4518,7 +4865,7 @@ function createTicketsCommand() {
4518
4865
  handleError(error, useJson);
4519
4866
  }
4520
4867
  });
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(
4868
+ 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
4869
  "--label <slug-or-id>",
4523
4870
  "Label to attach (repeatable)",
4524
4871
  collectRepeatable,
@@ -4548,6 +4895,7 @@ function createTicketsCommand() {
4548
4895
  featureId: options.feature,
4549
4896
  scopeId: options.scope,
4550
4897
  epicId: options.epic,
4898
+ cycleId: options.cycle,
4551
4899
  assigneeId: options.assignee,
4552
4900
  branchName: options.branch,
4553
4901
  prUrl: options.pr
@@ -4570,7 +4918,7 @@ function createTicketsCommand() {
4570
4918
  }
4571
4919
  }
4572
4920
  );
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(
4921
+ 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
4922
  "--add-label <slug-or-id>",
4575
4923
  "Label to attach to this ticket (repeatable)",
4576
4924
  collectRepeatable,
@@ -4617,6 +4965,7 @@ function createTicketsCommand() {
4617
4965
  points: parseNullableFloat(options.points),
4618
4966
  featureId: parseNullableString(options.feature),
4619
4967
  epicId: parseNullableString(options.epic),
4968
+ cycleId: parseNullableString(options.cycle),
4620
4969
  assigneeId: parseNullableString(options.assignee),
4621
4970
  branchName: parseNullableString(options.branch),
4622
4971
  prUrl: parseNullableString(options.pr)
@@ -4729,7 +5078,7 @@ function createTicketsCommand() {
4729
5078
  handleError(error, useJson);
4730
5079
  }
4731
5080
  });
4732
- const comment = new Command19("comment").description("Manage comments on a ticket");
5081
+ const comment = new Command20("comment").description("Manage comments on a ticket");
4733
5082
  comment.command("list").description("List comments on a ticket").requiredOption("--id <id>", "Ticket CUID").action(async (options, cmd) => {
4734
5083
  const globalOpts = cmd.optsWithGlobals();
4735
5084
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -4821,10 +5170,10 @@ function createTicketsCommand() {
4821
5170
  }
4822
5171
 
4823
5172
  // src/commands/workspaces.ts
4824
- import { Command as Command20 } from "commander";
4825
- import chalk7 from "chalk";
5173
+ import { Command as Command21 } from "commander";
5174
+ import chalk8 from "chalk";
4826
5175
  function createWorkspacesCommand() {
4827
- const workspaces = new Command20("workspaces").description("Manage workspaces");
5176
+ const workspaces = new Command21("workspaces").description("Manage workspaces");
4828
5177
  workspaces.command("list").description("List all workspaces").action(async (_options, cmd) => {
4829
5178
  const globalOpts = cmd.optsWithGlobals();
4830
5179
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -4837,7 +5186,7 @@ function createWorkspacesCommand() {
4837
5186
  const config = getConfig();
4838
5187
  outputWorkspacesPretty(workspaces2);
4839
5188
  if (config.defaultWorkspaceSlug) {
4840
- console.log(chalk7.gray(`Default workspace: ${config.defaultWorkspaceSlug}`));
5189
+ console.log(chalk8.gray(`Default workspace: ${config.defaultWorkspaceSlug}`));
4841
5190
  }
4842
5191
  }
4843
5192
  } catch (error) {
@@ -4850,10 +5199,10 @@ function createWorkspacesCommand() {
4850
5199
  const workspaces2 = await client.workspaces.list();
4851
5200
  const workspace = workspaces2.find((w) => w.slug === slug);
4852
5201
  if (!workspace) {
4853
- console.error(chalk7.red(`Workspace with slug "${slug}" not found.`));
4854
- console.log(chalk7.gray("Available workspaces:"));
5202
+ console.error(chalk8.red(`Workspace with slug "${slug}" not found.`));
5203
+ console.log(chalk8.gray("Available workspaces:"));
4855
5204
  workspaces2.forEach((w) => {
4856
- console.log(chalk7.gray(` - ${w.slug} (${w.name})`));
5205
+ console.log(chalk8.gray(` - ${w.slug} (${w.name})`));
4857
5206
  });
4858
5207
  process.exit(1);
4859
5208
  }
@@ -4861,7 +5210,7 @@ function createWorkspacesCommand() {
4861
5210
  defaultWorkspaceId: workspace.id,
4862
5211
  defaultWorkspaceSlug: workspace.slug
4863
5212
  });
4864
- console.log(chalk7.green(`Default workspace set to: ${workspace.name} (${workspace.slug})`));
5213
+ console.log(chalk8.green(`Default workspace set to: ${workspace.name} (${workspace.slug})`));
4865
5214
  } catch (error) {
4866
5215
  handleError(error);
4867
5216
  }
@@ -4889,20 +5238,20 @@ function createWorkspacesCommand() {
4889
5238
  return;
4890
5239
  }
4891
5240
  if (members.length === 0) {
4892
- console.log(chalk7.gray("No members found."));
5241
+ console.log(chalk8.gray("No members found."));
4893
5242
  return;
4894
5243
  }
4895
- console.log(chalk7.bold(`
5244
+ console.log(chalk8.bold(`
4896
5245
  Members (${members.length})
4897
5246
  `));
4898
5247
  for (const m of members) {
4899
5248
  const label = m.name ?? m.email ?? m.id;
4900
- const via = m.source === "team" ? chalk7.gray(
5249
+ const via = m.source === "team" ? chalk8.gray(
4901
5250
  ` via team${m.teams.length ? `: ${m.teams.map((t) => t.name).join(", ")}` : ""}` + (m.teamRole ? ` [team ${m.teamRole}]` : "")
4902
5251
  ) : "";
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}`));
5252
+ console.log(`${chalk8.bold(label)} ${chalk8.gray(`(${m.role})`)}${via}`);
5253
+ if (m.email) console.log(chalk8.gray(` ${m.email}`));
5254
+ console.log(chalk8.gray(` mention: ${m.mentionSyntax}`));
4906
5255
  console.log();
4907
5256
  }
4908
5257
  } catch (error) {
@@ -4914,7 +5263,7 @@ Members (${members.length})
4914
5263
  }
4915
5264
 
4916
5265
  // src/commands/goals.ts
4917
- import { Command as Command21 } from "commander";
5266
+ import { Command as Command22 } from "commander";
4918
5267
  var GOAL_STATUSES = [
4919
5268
  "planned",
4920
5269
  "active",
@@ -5026,7 +5375,7 @@ function validateKeyResultUnit(value) {
5026
5375
  return value;
5027
5376
  }
5028
5377
  function createKeyResultsCommand() {
5029
- const kr = new Command21("kr").description(
5378
+ const kr = new Command22("kr").description(
5030
5379
  "Key results on an objective. Ids are CUIDs; --goal takes the objective's integer id."
5031
5380
  );
5032
5381
  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 +5578,7 @@ function createKeyResultsCommand() {
5229
5578
  return kr;
5230
5579
  }
5231
5580
  function createGoalCommentsCommand() {
5232
- const comment = new Command21("comment").description(
5581
+ const comment = new Command22("comment").description(
5233
5582
  "Read and post comments on a goal. Mention teammates with --mention."
5234
5583
  );
5235
5584
  comment.command("list").description("List comments on a goal").requiredOption("--goal <id>", "Goal ID (a number)").action(async (options, cmd) => {
@@ -5324,7 +5673,7 @@ function createGoalCommentsCommand() {
5324
5673
  return comment;
5325
5674
  }
5326
5675
  function createGoalsCommand() {
5327
- const goals = new Command21("goals").description(
5676
+ const goals = new Command22("goals").description(
5328
5677
  "Manage goals (objectives) and their key results. Objectives have integer ids; key results are CUIDs."
5329
5678
  );
5330
5679
  goals.command("list").description(
@@ -5590,7 +5939,7 @@ Re-run with --with-key-results to go ahead, or move them with \`goals kr update
5590
5939
  return goals;
5591
5940
  }
5592
5941
  function createOkrsCommand() {
5593
- const okrs = new Command21("okrs").description(
5942
+ const okrs = new Command22("okrs").description(
5594
5943
  "OKRs \u2014 objectives with their key results nested. Manage the parts with `goals` and `goals kr`."
5595
5944
  );
5596
5945
  okrs.command("list").description(
@@ -5674,7 +6023,7 @@ import { fileURLToPath } from "url";
5674
6023
  var PKG_VERSION = JSON.parse(
5675
6024
  readFileSync2(join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf-8")
5676
6025
  ).version;
5677
- var program = new Command22();
6026
+ var program = new Command23();
5678
6027
  program.name("exponential").description(
5679
6028
  [
5680
6029
  "CLI to interact with Exponential productivity app.",
@@ -5691,6 +6040,7 @@ program.addCommand(createDealsCommand());
5691
6040
  program.addCommand(createEpicsCommand());
5692
6041
  program.addCommand(createFeaturesCommand());
5693
6042
  program.addCommand(createLabelsCommand());
6043
+ program.addCommand(createMeetingsCommand());
5694
6044
  program.addCommand(createOrganizationsCommand());
5695
6045
  program.addCommand(createPagesCommand());
5696
6046
  program.addCommand(createProductsCommand());