exponential-cli 1.11.2 → 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";
@@ -899,6 +899,17 @@ ${chalk3[getFeatureStatusColor(feature.status)](`[${feature.status}]`)} ${chalk3
899
899
  if (feature.priority != null) {
900
900
  console.log(` ${chalk3.magenta("Priority:")} ${feature.priority}`);
901
901
  }
902
+ if (feature.goal) {
903
+ console.log(` ${chalk3.blue("Objective:")} ${feature.goal.title} ${chalk3.gray(`(${feature.goal.id})`)}`);
904
+ }
905
+ if (feature.keyResultLinks?.length) {
906
+ console.log(` ${chalk3.blue("Key results:")}`);
907
+ for (const link of feature.keyResultLinks) {
908
+ const kr = link.keyResult;
909
+ const progress = kr.currentValue != null && kr.targetValue != null ? `${kr.currentValue}/${kr.targetValue}${kr.unit && kr.unit !== "custom" ? ` ${kr.unit}` : ""}, ` : "";
910
+ console.log(` \xB7 ${kr.title} ${chalk3.gray(`(${progress}${kr.period}, ${kr.id})`)}`);
911
+ }
912
+ }
902
913
  if (feature._count?.tickets != null) {
903
914
  console.log(` ${chalk3.cyan("Tickets:")} ${feature._count.tickets}`);
904
915
  }
@@ -923,9 +934,26 @@ Features (${features.length} total)`));
923
934
  const ticketCount = f._count?.tickets != null ? chalk3.gray(` \u2014 ${f._count.tickets} tickets`) : "";
924
935
  console.log(` ${statusBadge} ${chalk3.bold(f.name)}${ticketCount}`);
925
936
  console.log(chalk3.gray(` ID: ${f.id}`));
937
+ if (f.keyResultLinks?.length) {
938
+ const titles = f.keyResultLinks.map((l) => l.keyResult.title).join("; ");
939
+ console.log(chalk3.gray(` KRs: ${titles}`));
940
+ }
926
941
  }
927
942
  console.log();
928
943
  }
944
+ function summarizeFeatureKeyResult(link) {
945
+ const kr = link.keyResult;
946
+ return {
947
+ keyResultId: kr.id,
948
+ title: kr.title,
949
+ period: kr.period,
950
+ goalId: kr.goalId,
951
+ status: kr.status ?? null,
952
+ currentValue: kr.currentValue ?? null,
953
+ targetValue: kr.targetValue ?? null,
954
+ unit: kr.unit ?? null
955
+ };
956
+ }
929
957
  function transformFeature(feature) {
930
958
  return {
931
959
  id: feature.id,
@@ -938,6 +966,12 @@ function transformFeature(feature) {
938
966
  priority: feature.priority,
939
967
  goalId: feature.goalId,
940
968
  goal: feature.goal ?? null,
969
+ // Flattened out of the join row (ADR-0050): callers building a per-feature
970
+ // "which key results does this move" column want the key results, not the
971
+ // edge rows. `goal` above is the coarser Objective alignment and is a
972
+ // different question - the two are allowed to disagree, so both ride along.
973
+ // Progress values are present on `features get` and absent on `list`.
974
+ keyResults: feature.keyResultLinks?.map(summarizeFeatureKeyResult) ?? null,
941
975
  counts: feature._count ?? null,
942
976
  createdAt: new Date(feature.createdAt).toISOString(),
943
977
  updatedAt: new Date(feature.updatedAt).toISOString()
@@ -1324,6 +1358,157 @@ Pages (${pages.length} total)`));
1324
1358
  }
1325
1359
  console.log();
1326
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
+ }
1327
1512
  function outputRequirementJson(requirement) {
1328
1513
  console.log(JSON.stringify(requirement, null, 2));
1329
1514
  }
@@ -3770,8 +3955,211 @@ function createLabelsCommand() {
3770
3955
  return labels;
3771
3956
  }
3772
3957
 
3773
- // src/commands/organizations.ts
3958
+ // src/commands/meetings.ts
3774
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";
3775
4163
  var VALID_SIZES = [
3776
4164
  "1-10",
3777
4165
  "11-50",
@@ -3781,7 +4169,7 @@ var VALID_SIZES = [
3781
4169
  "1000+"
3782
4170
  ];
3783
4171
  function createOrganizationsCommand() {
3784
- const organizations = new Command13("organizations").alias("orgs").description("Manage CRM organizations");
4172
+ const organizations = new Command14("organizations").alias("orgs").description("Manage CRM organizations");
3785
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) => {
3786
4174
  const globalOpts = cmd.optsWithGlobals();
3787
4175
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -3849,13 +4237,12 @@ function createOrganizationsCommand() {
3849
4237
  }
3850
4238
 
3851
4239
  // src/commands/pages.ts
3852
- import { readFileSync } from "fs";
3853
- import { Command as Command15 } from "commander";
4240
+ import { Command as Command16 } from "commander";
3854
4241
 
3855
4242
  // src/commands/pageComments.ts
3856
- import { Command as Command14 } from "commander";
4243
+ import { Command as Command15 } from "commander";
3857
4244
  function createPageCommentsCommand() {
3858
- const comment = new Command14("comment").description(
4245
+ const comment = new Command15("comment").description(
3859
4246
  "Read and post comments on a page. Mention teammates with --mention."
3860
4247
  );
3861
4248
  comment.command("list").description("List comments on a page").requiredOption("--page <id>", "Page ID").action(async (options, cmd) => {
@@ -3949,14 +4336,8 @@ function createPageCommentsCommand() {
3949
4336
  }
3950
4337
 
3951
4338
  // src/commands/pages.ts
3952
- function readBody(body, bodyFile) {
3953
- if (body !== void 0) return body;
3954
- if (bodyFile === void 0) return void 0;
3955
- if (bodyFile === "-") return readFileSync(0, "utf-8");
3956
- return readFileSync(bodyFile, "utf-8");
3957
- }
3958
4339
  function createPagesCommand() {
3959
- const pages = new Command15("pages").description(
4340
+ const pages = new Command16("pages").description(
3960
4341
  'Manage Knowledge pages (PRDs, research, technical specs). Link one to a feature with "features link-page".'
3961
4342
  );
3962
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(
@@ -3999,7 +4380,7 @@ function createPagesCommand() {
3999
4380
  const page = await client.pages.create({
4000
4381
  workspaceId,
4001
4382
  title: options.title,
4002
- body: readBody(options.body, options.bodyFile)
4383
+ body: readText(options.body, options.bodyFile)
4003
4384
  });
4004
4385
  if (useJson) outputPageJson(page);
4005
4386
  else {
@@ -4022,7 +4403,7 @@ function createPagesCommand() {
4022
4403
  const page = await client.pages.update({
4023
4404
  id: options.id,
4024
4405
  title: options.title,
4025
- body: readBody(options.body, options.bodyFile)
4406
+ body: readText(options.body, options.bodyFile)
4026
4407
  });
4027
4408
  if (useJson) outputPageJson(page);
4028
4409
  else {
@@ -4039,9 +4420,9 @@ function createPagesCommand() {
4039
4420
  }
4040
4421
 
4041
4422
  // src/commands/products.ts
4042
- import { Command as Command16 } from "commander";
4423
+ import { Command as Command17 } from "commander";
4043
4424
  function createProductsCommand() {
4044
- const products = new Command16("products").description(
4425
+ const products = new Command17("products").description(
4045
4426
  "Manage products. A workspace contains products; a product holds features and tickets."
4046
4427
  );
4047
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) => {
@@ -4104,7 +4485,7 @@ function createProductsCommand() {
4104
4485
  }
4105
4486
 
4106
4487
  // src/commands/projects.ts
4107
- import { Command as Command17 } from "commander";
4488
+ import { Command as Command18 } from "commander";
4108
4489
  var PROJECT_STATUSES = [
4109
4490
  "ACTIVE",
4110
4491
  "ON_HOLD",
@@ -4131,7 +4512,7 @@ function validateProjectPriority(value) {
4131
4512
  return value;
4132
4513
  }
4133
4514
  function createProjectsCommand() {
4134
- const projects = new Command17("projects").description("Manage projects");
4515
+ const projects = new Command18("projects").description("Manage projects");
4135
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) => {
4136
4517
  const globalOpts = cmd.optsWithGlobals();
4137
4518
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -4250,8 +4631,8 @@ Re-run with --force to delete it anyway.`
4250
4631
  }
4251
4632
 
4252
4633
  // src/commands/search.ts
4253
- import { Command as Command18 } from "commander";
4254
- import chalk6 from "chalk";
4634
+ import { Command as Command19 } from "commander";
4635
+ import chalk7 from "chalk";
4255
4636
  var TYPE_LABELS = {
4256
4637
  workspace: "Workspaces",
4257
4638
  project: "Projects",
@@ -4269,7 +4650,7 @@ var TYPE_LABELS = {
4269
4650
  product: "Products"
4270
4651
  };
4271
4652
  function createSearchCommand() {
4272
- const search = new Command18("search").description(
4653
+ const search = new Command19("search").description(
4273
4654
  "Global search across projects, actions, goals, workspaces and more \u2014 same coverage as the app's Cmd+K palette"
4274
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) => {
4275
4656
  const globalOpts = cmd.optsWithGlobals();
@@ -4283,10 +4664,10 @@ function createSearchCommand() {
4283
4664
  (w) => w.slug === options.workspace || w.id === options.workspace
4284
4665
  );
4285
4666
  if (!match) {
4286
- console.error(chalk6.red(`Workspace "${options.workspace}" not found.`));
4287
- console.log(chalk6.gray("Available workspaces:"));
4667
+ console.error(chalk7.red(`Workspace "${options.workspace}" not found.`));
4668
+ console.log(chalk7.gray("Available workspaces:"));
4288
4669
  workspaces.forEach((w) => {
4289
- console.log(chalk6.gray(` - ${w.slug} (${w.name})`));
4670
+ console.log(chalk7.gray(` - ${w.slug} (${w.name})`));
4290
4671
  });
4291
4672
  process.exit(1);
4292
4673
  }
@@ -4299,7 +4680,7 @@ function createSearchCommand() {
4299
4680
  return;
4300
4681
  }
4301
4682
  if (result.results.length === 0) {
4302
- console.log(chalk6.gray(`No matches for "${result.query}".`));
4683
+ console.log(chalk7.gray(`No matches for "${result.query}".`));
4303
4684
  return;
4304
4685
  }
4305
4686
  const byType = /* @__PURE__ */ new Map();
@@ -4309,14 +4690,14 @@ function createSearchCommand() {
4309
4690
  byType.set(r.type, bucket);
4310
4691
  }
4311
4692
  for (const [type, rows] of byType) {
4312
- console.log(chalk6.bold(`
4693
+ console.log(chalk7.bold(`
4313
4694
  ${TYPE_LABELS[type] ?? type}`));
4314
4695
  for (const row of rows) {
4315
- const parts = [chalk6.white(row.title)];
4316
- if (row.subtitle) parts.push(chalk6.gray(row.subtitle));
4317
- if (row.workspace) parts.push(chalk6.cyan(`@${row.workspace.slug}`));
4318
- console.log(` ${parts.join(chalk6.gray(" \xB7 "))}`);
4319
- 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}` : ""}`));
4320
4701
  }
4321
4702
  }
4322
4703
  console.log();
@@ -4328,7 +4709,7 @@ ${TYPE_LABELS[type] ?? type}`));
4328
4709
  }
4329
4710
 
4330
4711
  // src/commands/tickets.ts
4331
- import { Command as Command19 } from "commander";
4712
+ import { Command as Command20 } from "commander";
4332
4713
  function collectRepeatable(value, previous) {
4333
4714
  return [...previous, value];
4334
4715
  }
@@ -4371,7 +4752,7 @@ function validateTicketType(value) {
4371
4752
  return value;
4372
4753
  }
4373
4754
  function createTicketsCommand() {
4374
- const tickets = new Command19("tickets").description(
4755
+ const tickets = new Command20("tickets").description(
4375
4756
  "Manage product backlog tickets. Tickets live under a product, link to features/epics, depend on each other, and can be implemented via actions."
4376
4757
  );
4377
4758
  tickets.command("list").description(
@@ -4484,7 +4865,7 @@ function createTicketsCommand() {
4484
4865
  handleError(error, useJson);
4485
4866
  }
4486
4867
  });
4487
- 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(
4488
4869
  "--label <slug-or-id>",
4489
4870
  "Label to attach (repeatable)",
4490
4871
  collectRepeatable,
@@ -4514,6 +4895,7 @@ function createTicketsCommand() {
4514
4895
  featureId: options.feature,
4515
4896
  scopeId: options.scope,
4516
4897
  epicId: options.epic,
4898
+ cycleId: options.cycle,
4517
4899
  assigneeId: options.assignee,
4518
4900
  branchName: options.branch,
4519
4901
  prUrl: options.pr
@@ -4536,7 +4918,7 @@ function createTicketsCommand() {
4536
4918
  }
4537
4919
  }
4538
4920
  );
4539
- 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(
4540
4922
  "--add-label <slug-or-id>",
4541
4923
  "Label to attach to this ticket (repeatable)",
4542
4924
  collectRepeatable,
@@ -4583,6 +4965,7 @@ function createTicketsCommand() {
4583
4965
  points: parseNullableFloat(options.points),
4584
4966
  featureId: parseNullableString(options.feature),
4585
4967
  epicId: parseNullableString(options.epic),
4968
+ cycleId: parseNullableString(options.cycle),
4586
4969
  assigneeId: parseNullableString(options.assignee),
4587
4970
  branchName: parseNullableString(options.branch),
4588
4971
  prUrl: parseNullableString(options.pr)
@@ -4695,7 +5078,7 @@ function createTicketsCommand() {
4695
5078
  handleError(error, useJson);
4696
5079
  }
4697
5080
  });
4698
- const comment = new Command19("comment").description("Manage comments on a ticket");
5081
+ const comment = new Command20("comment").description("Manage comments on a ticket");
4699
5082
  comment.command("list").description("List comments on a ticket").requiredOption("--id <id>", "Ticket CUID").action(async (options, cmd) => {
4700
5083
  const globalOpts = cmd.optsWithGlobals();
4701
5084
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -4787,10 +5170,10 @@ function createTicketsCommand() {
4787
5170
  }
4788
5171
 
4789
5172
  // src/commands/workspaces.ts
4790
- import { Command as Command20 } from "commander";
4791
- import chalk7 from "chalk";
5173
+ import { Command as Command21 } from "commander";
5174
+ import chalk8 from "chalk";
4792
5175
  function createWorkspacesCommand() {
4793
- const workspaces = new Command20("workspaces").description("Manage workspaces");
5176
+ const workspaces = new Command21("workspaces").description("Manage workspaces");
4794
5177
  workspaces.command("list").description("List all workspaces").action(async (_options, cmd) => {
4795
5178
  const globalOpts = cmd.optsWithGlobals();
4796
5179
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -4803,7 +5186,7 @@ function createWorkspacesCommand() {
4803
5186
  const config = getConfig();
4804
5187
  outputWorkspacesPretty(workspaces2);
4805
5188
  if (config.defaultWorkspaceSlug) {
4806
- console.log(chalk7.gray(`Default workspace: ${config.defaultWorkspaceSlug}`));
5189
+ console.log(chalk8.gray(`Default workspace: ${config.defaultWorkspaceSlug}`));
4807
5190
  }
4808
5191
  }
4809
5192
  } catch (error) {
@@ -4816,10 +5199,10 @@ function createWorkspacesCommand() {
4816
5199
  const workspaces2 = await client.workspaces.list();
4817
5200
  const workspace = workspaces2.find((w) => w.slug === slug);
4818
5201
  if (!workspace) {
4819
- console.error(chalk7.red(`Workspace with slug "${slug}" not found.`));
4820
- console.log(chalk7.gray("Available workspaces:"));
5202
+ console.error(chalk8.red(`Workspace with slug "${slug}" not found.`));
5203
+ console.log(chalk8.gray("Available workspaces:"));
4821
5204
  workspaces2.forEach((w) => {
4822
- console.log(chalk7.gray(` - ${w.slug} (${w.name})`));
5205
+ console.log(chalk8.gray(` - ${w.slug} (${w.name})`));
4823
5206
  });
4824
5207
  process.exit(1);
4825
5208
  }
@@ -4827,7 +5210,7 @@ function createWorkspacesCommand() {
4827
5210
  defaultWorkspaceId: workspace.id,
4828
5211
  defaultWorkspaceSlug: workspace.slug
4829
5212
  });
4830
- 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})`));
4831
5214
  } catch (error) {
4832
5215
  handleError(error);
4833
5216
  }
@@ -4855,20 +5238,20 @@ function createWorkspacesCommand() {
4855
5238
  return;
4856
5239
  }
4857
5240
  if (members.length === 0) {
4858
- console.log(chalk7.gray("No members found."));
5241
+ console.log(chalk8.gray("No members found."));
4859
5242
  return;
4860
5243
  }
4861
- console.log(chalk7.bold(`
5244
+ console.log(chalk8.bold(`
4862
5245
  Members (${members.length})
4863
5246
  `));
4864
5247
  for (const m of members) {
4865
5248
  const label = m.name ?? m.email ?? m.id;
4866
- const via = m.source === "team" ? chalk7.gray(
5249
+ const via = m.source === "team" ? chalk8.gray(
4867
5250
  ` via team${m.teams.length ? `: ${m.teams.map((t) => t.name).join(", ")}` : ""}` + (m.teamRole ? ` [team ${m.teamRole}]` : "")
4868
5251
  ) : "";
4869
- console.log(`${chalk7.bold(label)} ${chalk7.gray(`(${m.role})`)}${via}`);
4870
- if (m.email) console.log(chalk7.gray(` ${m.email}`));
4871
- 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}`));
4872
5255
  console.log();
4873
5256
  }
4874
5257
  } catch (error) {
@@ -4880,7 +5263,7 @@ Members (${members.length})
4880
5263
  }
4881
5264
 
4882
5265
  // src/commands/goals.ts
4883
- import { Command as Command21 } from "commander";
5266
+ import { Command as Command22 } from "commander";
4884
5267
  var GOAL_STATUSES = [
4885
5268
  "planned",
4886
5269
  "active",
@@ -4992,7 +5375,7 @@ function validateKeyResultUnit(value) {
4992
5375
  return value;
4993
5376
  }
4994
5377
  function createKeyResultsCommand() {
4995
- const kr = new Command21("kr").description(
5378
+ const kr = new Command22("kr").description(
4996
5379
  "Key results on an objective. Ids are CUIDs; --goal takes the objective's integer id."
4997
5380
  );
4998
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(
@@ -5195,7 +5578,7 @@ function createKeyResultsCommand() {
5195
5578
  return kr;
5196
5579
  }
5197
5580
  function createGoalCommentsCommand() {
5198
- const comment = new Command21("comment").description(
5581
+ const comment = new Command22("comment").description(
5199
5582
  "Read and post comments on a goal. Mention teammates with --mention."
5200
5583
  );
5201
5584
  comment.command("list").description("List comments on a goal").requiredOption("--goal <id>", "Goal ID (a number)").action(async (options, cmd) => {
@@ -5290,7 +5673,7 @@ function createGoalCommentsCommand() {
5290
5673
  return comment;
5291
5674
  }
5292
5675
  function createGoalsCommand() {
5293
- const goals = new Command21("goals").description(
5676
+ const goals = new Command22("goals").description(
5294
5677
  "Manage goals (objectives) and their key results. Objectives have integer ids; key results are CUIDs."
5295
5678
  );
5296
5679
  goals.command("list").description(
@@ -5556,7 +5939,7 @@ Re-run with --with-key-results to go ahead, or move them with \`goals kr update
5556
5939
  return goals;
5557
5940
  }
5558
5941
  function createOkrsCommand() {
5559
- const okrs = new Command21("okrs").description(
5942
+ const okrs = new Command22("okrs").description(
5560
5943
  "OKRs \u2014 objectives with their key results nested. Manage the parts with `goals` and `goals kr`."
5561
5944
  );
5562
5945
  okrs.command("list").description(
@@ -5640,7 +6023,7 @@ import { fileURLToPath } from "url";
5640
6023
  var PKG_VERSION = JSON.parse(
5641
6024
  readFileSync2(join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf-8")
5642
6025
  ).version;
5643
- var program = new Command22();
6026
+ var program = new Command23();
5644
6027
  program.name("exponential").description(
5645
6028
  [
5646
6029
  "CLI to interact with Exponential productivity app.",
@@ -5657,6 +6040,7 @@ program.addCommand(createDealsCommand());
5657
6040
  program.addCommand(createEpicsCommand());
5658
6041
  program.addCommand(createFeaturesCommand());
5659
6042
  program.addCommand(createLabelsCommand());
6043
+ program.addCommand(createMeetingsCommand());
5660
6044
  program.addCommand(createOrganizationsCommand());
5661
6045
  program.addCommand(createPagesCommand());
5662
6046
  program.addCommand(createProductsCommand());