exponential-cli 1.11.0 → 1.11.2

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/README.md CHANGED
@@ -130,14 +130,69 @@ exponential actions reschedule --ids id1,id2 --to 2026-08-10
130
130
  > would report 6 actions while 43 sat overdue. Pass `--due-only` for the
131
131
  > previous output shape.
132
132
 
133
+ ### Goals and OKRs
134
+
135
+ Objectives are goals; they carry **integer** ids. Their key results carry CUIDs
136
+ and live under `goals kr`.
137
+
138
+ ```bash
139
+ # List a workspace's objectives, or the annual → quarterly cascade
140
+ exponential goals list --workspace clear --period Q3-2026
141
+ exponential goals list --workspace clear --tree
142
+ exponential goals list --all-workspaces # "what am I neglecting" is cross-workspace
143
+
144
+ exponential goals get 46
145
+ exponential goals create --workspace clear --title "Ship the CLI" --period Q3-2026
146
+
147
+ # Close a quarter. set-status/close write ONLY the status column.
148
+ exponential goals close --id 46 # → completed
149
+ exponential goals set-status --id 46 --status on-hold
150
+
151
+ # Everything else is a partial update: fields you don't pass are left alone,
152
+ # and "none" is how you clear one.
153
+ exponential goals update --id 46 --title "Ship the CLI, properly"
154
+ exponential goals update --id 46 --workspace none # make it personal
155
+ exponential goals reparent --id 47 --parent 46 # or --parent none
156
+ exponential goals delete --id 46 # refuses if it has key results
157
+
158
+ exponential goals periods
159
+ exponential goals stats --workspace clear --period Q3-2026
160
+
161
+ # The quarter at a glance: objectives with their key results nested
162
+ exponential okrs list --workspace clear
163
+ ```
164
+
165
+ Key results:
166
+
167
+ ```bash
168
+ exponential goals kr list --goal 46
169
+ exponential goals kr list --workspace clear --status at-risk
170
+ exponential goals kr create --goal 46 --title "Weekly active agents" --target 500 --unit count
171
+ exponential goals kr checkin --id <cuid> --value 120 --note "post-launch bump"
172
+ exponential goals kr update --id <cuid> --target 600
173
+ exponential goals kr link --id <cuid> --feature <feature-cuid> # or --project <cuid>
174
+ exponential goals kr unlink --id <cuid> --project <cuid>
175
+ exponential goals kr delete --id <cuid>
176
+ ```
177
+
133
178
  ### Projects
134
179
 
135
180
  ```bash
136
181
  # List all projects
137
182
  exponential projects list
138
183
 
139
- # List projects in a workspace
140
- exponential projects list --workspace <workspace-id>
184
+ # List projects in a workspace (slug or CUID)
185
+ exponential projects list --workspace clear
186
+
187
+ # One project, with its objectives, key results, DRI and actions
188
+ exponential projects get <cuid|slug>
189
+
190
+ # Partial update — unnamed fields are preserved
191
+ exponential projects update --id <cuid> --name "KR support" --status COMPLETED
192
+ exponential projects update --id <cuid> --product none
193
+
194
+ # Refuses while the project still has actions or OKR links
195
+ exponential projects delete --id <cuid> [--force]
141
196
  ```
142
197
 
143
198
  ### Workspaces
package/dist/index.js CHANGED
@@ -1748,6 +1748,63 @@ OKRs (${objectives.length} objectives, ${totalKeyResults} key results)`)
1748
1748
  }
1749
1749
  console.log();
1750
1750
  }
1751
+ function outputProjectJson(project) {
1752
+ console.log(JSON.stringify({
1753
+ ...transformProject(project),
1754
+ // The detail read has no `workspace` relation to expand, unlike the list
1755
+ // read — so `transformProject` alone would emit `workspace: null` and no id
1756
+ // at all, which reads as "personal project" rather than "not fetched".
1757
+ workspaceId: project.workspaceId,
1758
+ slug: project.slug ?? null,
1759
+ productId: project.productId ?? null,
1760
+ driId: project.driId ?? null,
1761
+ dri: project.dri ?? null,
1762
+ team: project.team ?? null,
1763
+ goals: project.goals ?? [],
1764
+ keyResults: project.keyResults?.map((k) => ({
1765
+ id: k.keyResultId,
1766
+ title: k.keyResult?.title ?? null,
1767
+ goal: k.keyResult?.goal ?? null
1768
+ })) ?? [],
1769
+ outcomes: project.outcomes ?? [],
1770
+ actionCount: project.actions?.length ?? null,
1771
+ createdAt: project.createdAt ? new Date(project.createdAt).toISOString() : null
1772
+ }, null, 2));
1773
+ }
1774
+ function outputProjectPretty(project) {
1775
+ console.log(chalk3.gray("\u2500".repeat(50)));
1776
+ console.log(`
1777
+ ${chalk3.bold(project.name)}`);
1778
+ console.log(chalk3.gray(` ID: ${project.id}`));
1779
+ if (project.status) console.log(` ${chalk3.cyan("Status:")} ${project.status}`);
1780
+ if (project.priority) console.log(` ${chalk3.magenta("Priority:")} ${project.priority}`);
1781
+ if (project.workspace) {
1782
+ console.log(` ${chalk3.yellow("Workspace:")} ${project.workspace.name} (${project.workspace.slug})`);
1783
+ }
1784
+ if (project.description) {
1785
+ console.log(` ${chalk3.gray("Description:")} ${project.description.substring(0, 200)}${project.description.length > 200 ? "..." : ""}`);
1786
+ }
1787
+ if (project.dri) {
1788
+ console.log(` ${chalk3.magenta("DRI:")} ${project.dri.name ?? project.dri.email ?? project.dri.id}`);
1789
+ }
1790
+ if (project.goals?.length) {
1791
+ console.log(` ${chalk3.cyan("Objectives:")}`);
1792
+ for (const g of project.goals) {
1793
+ console.log(` - #${g.id} ${g.title}`);
1794
+ }
1795
+ }
1796
+ if (project.keyResults?.length) {
1797
+ console.log(` ${chalk3.cyan("Key results:")}`);
1798
+ for (const k of project.keyResults) {
1799
+ const objective = k.keyResult?.goal ? chalk3.gray(` (objective #${k.keyResult.goal.id})`) : "";
1800
+ console.log(` - ${k.keyResult?.title ?? k.keyResultId} ${chalk3.gray(`[${k.keyResultId}]`)}${objective}`);
1801
+ }
1802
+ }
1803
+ if (project.actions?.length) {
1804
+ console.log(` ${chalk3.cyan("Actions:")} ${project.actions.length}`);
1805
+ }
1806
+ console.log();
1807
+ }
1751
1808
 
1752
1809
  // src/commands/actions.ts
1753
1810
  var VALID_PRIORITIES = [
@@ -3279,6 +3336,17 @@ function summarizeTicket(ticket) {
3279
3336
  function ticketLabel(ticket) {
3280
3337
  return ticket.shortId ?? (ticket.number != null ? `#${ticket.number}` : ticket.id);
3281
3338
  }
3339
+ function parseFeatureGoalId(value) {
3340
+ if (value === void 0) return void 0;
3341
+ if (value === "none") return null;
3342
+ const id = Number(value);
3343
+ if (!Number.isInteger(id)) {
3344
+ throw new Error(
3345
+ `--goal must be a whole number or "none", got "${value}". Find one with \`exponential goals list\`.`
3346
+ );
3347
+ }
3348
+ return id;
3349
+ }
3282
3350
  function validateFeatureStatus(value) {
3283
3351
  if (!value) return void 0;
3284
3352
  if (!FEATURE_STATUSES.includes(value)) {
@@ -3358,7 +3426,10 @@ function createFeaturesCommand() {
3358
3426
  }
3359
3427
  }
3360
3428
  );
3361
- features.command("update").description("Update an existing feature").requiredOption("--id <id>", "Feature CUID").option("-n, --name <name>", "New name").option("-d, --description <text>", "New description").option("--vision <text>", "New vision text").option("--status <status>", `Status: ${FEATURE_STATUSES.join(", ")}`).option("--priority <n>", "Priority 0-4", parseInt).option("--effort <n>", "Effort estimate", parseFloat).option("--area <id>", 'Area CUID ("none" to clear)').action(
3429
+ features.command("update").description("Update an existing feature").requiredOption("--id <id>", "Feature CUID").option("-n, --name <name>", "New name").option("-d, --description <text>", "New description").option("--vision <text>", "New vision text").option("--status <status>", `Status: ${FEATURE_STATUSES.join(", ")}`).option("--priority <n>", "Priority 0-4", parseInt).option("--effort <n>", "Effort estimate", parseFloat).option("--area <id>", 'Area CUID ("none" to clear)').option(
3430
+ "--goal <id|none>",
3431
+ 'Objective (goal) id this feature serves ("none" to clear). For progress rollup, link the finer Feature\u2192key-result edge with `goals kr link --feature`.'
3432
+ ).action(
3362
3433
  async (options, cmd) => {
3363
3434
  const globalOpts = cmd.optsWithGlobals();
3364
3435
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -3373,7 +3444,8 @@ function createFeaturesCommand() {
3373
3444
  status,
3374
3445
  priority: options.priority,
3375
3446
  effort: options.effort,
3376
- areaId: options.area === "none" ? null : options.area
3447
+ areaId: options.area === "none" ? null : options.area,
3448
+ goalId: parseFeatureGoalId(options.goal)
3377
3449
  });
3378
3450
  if (useJson) outputFeatureJson(feature);
3379
3451
  else {
@@ -4033,15 +4105,41 @@ function createProductsCommand() {
4033
4105
 
4034
4106
  // src/commands/projects.ts
4035
4107
  import { Command as Command17 } from "commander";
4108
+ var PROJECT_STATUSES = [
4109
+ "ACTIVE",
4110
+ "ON_HOLD",
4111
+ "COMPLETED",
4112
+ "CANCELLED"
4113
+ ];
4114
+ var PROJECT_PRIORITIES = ["HIGH", "MEDIUM", "LOW", "NONE"];
4115
+ function validateProjectStatus(value) {
4116
+ if (!value) return void 0;
4117
+ if (!PROJECT_STATUSES.includes(value)) {
4118
+ throw new Error(
4119
+ `Invalid project status "${value}". Valid: ${PROJECT_STATUSES.join(", ")}`
4120
+ );
4121
+ }
4122
+ return value;
4123
+ }
4124
+ function validateProjectPriority(value) {
4125
+ if (!value) return void 0;
4126
+ if (!PROJECT_PRIORITIES.includes(value)) {
4127
+ throw new Error(
4128
+ `Invalid project priority "${value}". Valid: ${PROJECT_PRIORITIES.join(", ")}`
4129
+ );
4130
+ }
4131
+ return value;
4132
+ }
4036
4133
  function createProjectsCommand() {
4037
4134
  const projects = new Command17("projects").description("Manage projects");
4038
- projects.command("list").description("List all projects").option("--workspace <id>", "Filter by workspace ID").option("--include-actions", "Include actions in output").action(async (options, cmd) => {
4135
+ 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) => {
4039
4136
  const globalOpts = cmd.optsWithGlobals();
4040
4137
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
4041
4138
  try {
4042
4139
  const client = getClient();
4140
+ const workspaceId = options.workspace ? await resolveWorkspaceId(client, options.workspace) : void 0;
4043
4141
  const projects2 = await client.projects.list({
4044
- workspaceId: options.workspace,
4142
+ workspaceId,
4045
4143
  includeActions: options.includeActions
4046
4144
  });
4047
4145
  if (useJson) {
@@ -4053,6 +4151,101 @@ function createProjectsCommand() {
4053
4151
  handleError(error, useJson);
4054
4152
  }
4055
4153
  });
4154
+ projects.command("get <id>").description(
4155
+ "Get one project with its objectives, key results, DRI and actions \u2014 none of which `projects list` carries. Accepts a CUID, a slug, or the slug-cuid form from app URLs."
4156
+ ).action(async (id, _options, cmd) => {
4157
+ const globalOpts = cmd.optsWithGlobals();
4158
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
4159
+ try {
4160
+ const client = getClient();
4161
+ const project = await client.projects.get(id);
4162
+ if (useJson) outputProjectJson(project);
4163
+ else outputProjectPretty(project);
4164
+ } catch (error) {
4165
+ handleError(error, useJson);
4166
+ }
4167
+ });
4168
+ projects.command("update").description(
4169
+ "Update a project. Only the fields you pass are written; the rest are read back from the project and re-sent unchanged."
4170
+ ).requiredOption("--id <cuid>", "Project CUID").option("-n, --name <name>", "New name").option("-d, --description <text>", "New description").option("--status <status>", `Status: ${PROJECT_STATUSES.join(", ")}`).option("--priority <priority>", `Priority: ${PROJECT_PRIORITIES.join(", ")}`).option("--product <cuid|none>", 'Product to file the project under ("none" to unlink)').option("--dri <userId|none>", 'Directly responsible individual ("none" to clear)').action(
4171
+ async (options, cmd) => {
4172
+ const globalOpts = cmd.optsWithGlobals();
4173
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
4174
+ try {
4175
+ const status = validateProjectStatus(options.status);
4176
+ const priority = validateProjectPriority(options.priority);
4177
+ const client = getClient();
4178
+ const updated = await client.projects.update({
4179
+ id: options.id,
4180
+ name: options.name,
4181
+ description: options.description,
4182
+ status,
4183
+ priority,
4184
+ productId: options.product === void 0 ? void 0 : options.product === "none" ? null : options.product,
4185
+ driId: options.dri === void 0 ? void 0 : options.dri === "none" ? null : options.dri
4186
+ });
4187
+ if (useJson) outputProjectJson(updated);
4188
+ else {
4189
+ console.log("\n\u2713 Project updated");
4190
+ outputProjectPretty(updated);
4191
+ }
4192
+ } catch (error) {
4193
+ handleError(error, useJson);
4194
+ }
4195
+ }
4196
+ );
4197
+ projects.command("delete").description(
4198
+ "Delete a project. Refuses while it still has actions or is linked to an objective or key result, unless --force."
4199
+ ).requiredOption("--id <cuid>", "Project CUID").option("--force", "Delete anyway, taking the actions and links with it").action(async (options, cmd) => {
4200
+ const globalOpts = cmd.optsWithGlobals();
4201
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
4202
+ try {
4203
+ const client = getClient();
4204
+ const project = await client.projects.get(options.id);
4205
+ const actions = project.actions ?? [];
4206
+ const goals = project.goals ?? [];
4207
+ const keyResults = project.keyResults ?? [];
4208
+ const blockers = [
4209
+ actions.length ? `${actions.length} action(s)` : null,
4210
+ goals.length ? `${goals.length} objective link(s)` : null,
4211
+ keyResults.length ? `${keyResults.length} key result link(s)` : null
4212
+ ].filter(Boolean);
4213
+ if (blockers.length > 0 && !options.force) {
4214
+ const payload2 = {
4215
+ deleted: false,
4216
+ id: project.id,
4217
+ reason: `has ${blockers.join(", ")}`,
4218
+ actionCount: actions.length,
4219
+ goals: goals.map((g) => ({ id: g.id, title: g.title })),
4220
+ keyResults: keyResults.map((k) => ({ id: k.keyResultId }))
4221
+ };
4222
+ if (useJson) console.log(JSON.stringify(payload2, null, 2));
4223
+ else {
4224
+ console.error(
4225
+ `Project "${project.name}" has ${blockers.join(", ")}.
4226
+ Re-run with --force to delete it anyway.`
4227
+ );
4228
+ }
4229
+ process.exitCode = 1;
4230
+ return;
4231
+ }
4232
+ await client.projects.delete(project.id);
4233
+ const payload = {
4234
+ deleted: true,
4235
+ id: project.id,
4236
+ actionsDeleted: actions.length
4237
+ };
4238
+ if (useJson) console.log(JSON.stringify(payload, null, 2));
4239
+ else {
4240
+ console.log("\u2713 Project deleted");
4241
+ if (actions.length) {
4242
+ console.log(` ${actions.length} action(s) went with it`);
4243
+ }
4244
+ }
4245
+ } catch (error) {
4246
+ handleError(error, useJson);
4247
+ }
4248
+ });
4056
4249
  return projects;
4057
4250
  }
4058
4251
 
@@ -4211,18 +4404,8 @@ function createTicketsCommand() {
4211
4404
  );
4212
4405
  }
4213
4406
  const client = getClient();
4214
- let workspaceId;
4215
- let productId;
4216
- if (options.product) {
4217
- workspaceId = await resolveWorkspaceId(client, options.workspace);
4218
- productId = await resolveProductId(
4219
- client,
4220
- workspaceId,
4221
- options.product
4222
- );
4223
- }
4224
- const list = await client.tickets.list({
4225
- productId,
4407
+ const workspaceId = await resolveWorkspaceId(client, options.workspace);
4408
+ const filters = {
4226
4409
  status,
4227
4410
  type,
4228
4411
  featureId: options.feature,
@@ -4230,7 +4413,27 @@ function createTicketsCommand() {
4230
4413
  assigneeId: options.assignee,
4231
4414
  prUrl: options.pr,
4232
4415
  branchName: options.branch
4233
- });
4416
+ };
4417
+ let list;
4418
+ if (options.product) {
4419
+ const productId = await resolveProductId(
4420
+ client,
4421
+ workspaceId,
4422
+ options.product
4423
+ );
4424
+ list = await client.tickets.list({ productId, ...filters });
4425
+ } else {
4426
+ const products = await client.products.list(workspaceId);
4427
+ const settled = await Promise.allSettled(
4428
+ products.map((p) => client.tickets.list({ productId: p.id, ...filters }))
4429
+ );
4430
+ list = settled.flatMap((r) => r.status === "fulfilled" ? r.value : []);
4431
+ settled.forEach((r, i) => {
4432
+ if (r.status !== "rejected") return;
4433
+ const reason = r.reason instanceof Error ? r.reason.message : String(r.reason);
4434
+ console.error(`Warning: skipped product ${products[i].slug}: ${reason}`);
4435
+ });
4436
+ }
4234
4437
  let filtered = list;
4235
4438
  if (options.label.length > 0) {
4236
4439
  const labelIds = new Set(
@@ -4799,10 +5002,17 @@ function createKeyResultsCommand() {
4799
5002
  try {
4800
5003
  const status = validateKeyResultStatus(options.status);
4801
5004
  const client = getClient();
4802
- const workspaceId = options.workspace ? await resolveWorkspaceId(client, options.workspace) : void 0;
5005
+ const goalId = options.goal ? parseGoalId(options.goal) : void 0;
5006
+ let workspaceId;
5007
+ if (options.workspace) {
5008
+ workspaceId = await resolveWorkspaceId(client, options.workspace);
5009
+ } else if (goalId !== void 0) {
5010
+ const goal = await client.goals.get(goalId);
5011
+ workspaceId = goal.workspaceId ?? void 0;
5012
+ }
4803
5013
  const list = await client.goals.keyResults.list({
4804
5014
  workspaceId,
4805
- goalId: options.goal ? parseGoalId(options.goal) : void 0,
5015
+ goalId,
4806
5016
  period: options.period,
4807
5017
  status,
4808
5018
  onlyMine: options.mine
@@ -5160,15 +5370,25 @@ function createGoalsCommand() {
5160
5370
  );
5161
5371
  goals.command("update").description(
5162
5372
  "Update an objective. Only the flags you pass are written \u2014 everything else is left exactly as it is. For a status change use `set-status`; to re-parent use `reparent`."
5163
- ).requiredOption("--id <n>", "Objective ID (a number)").option("-t, --title <text>", "New title").option("-d, --description <text>", 'New description ("none" clears it)').option("--why <text>", 'Why this goal matters ("none" clears it)').option("--period <period>", 'New period ("none" clears it)').option("--dri <userId>", "Directly responsible individual (user ID)").option("--status <status>", `Status: ${GOAL_WRITABLE_STATUSES.join(", ")}`).option("--project <cuid>", 'Replace the project links with this project ("none" clears them)').action(
5373
+ ).requiredOption("--id <n>", "Objective ID (a number)").option("-t, --title <text>", "New title").option("-d, --description <text>", 'New description ("none" clears it)').option("--why <text>", 'Why this goal matters ("none" clears it)').option("--period <period>", 'New period ("none" clears it)').option("--dri <userId>", "Directly responsible individual (user ID)").option("--status <status>", `Status: ${GOAL_WRITABLE_STATUSES.join(", ")}`).option("--project <cuid>", 'Replace the project links with this project ("none" clears them)').option(
5374
+ "-w, --workspace <slug|id|none>",
5375
+ 'Re-home the objective; "none" makes it personal'
5376
+ ).action(
5164
5377
  async (options, cmd) => {
5165
5378
  const globalOpts = cmd.optsWithGlobals();
5166
5379
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
5167
5380
  try {
5168
5381
  const status = validateWritableGoalStatus(options.status);
5169
5382
  const clearable = (value) => value === void 0 ? void 0 : value === "none" ? null : value;
5170
- const goal = await getClient().goals.update({
5383
+ const client = getClient();
5384
+ let workspaceId;
5385
+ if (options.workspace === "none") workspaceId = null;
5386
+ else if (options.workspace !== void 0) {
5387
+ workspaceId = await resolveWorkspaceId(client, options.workspace);
5388
+ }
5389
+ const goal = await client.goals.update({
5171
5390
  id: parseGoalId(options.id),
5391
+ workspaceId,
5172
5392
  title: options.title,
5173
5393
  description: clearable(options.description),
5174
5394
  whyThisGoal: clearable(options.why),
@@ -5248,21 +5468,60 @@ function createGoalsCommand() {
5248
5468
  handleError(error, useJson);
5249
5469
  }
5250
5470
  });
5251
- goals.command("delete").description("Delete an objective").requiredOption("--id <n>", "Objective ID (a number)").action(async (options, cmd) => {
5252
- const globalOpts = cmd.optsWithGlobals();
5253
- const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
5254
- try {
5255
- const id = parseGoalId(options.id);
5256
- await getClient().goals.delete(id);
5257
- if (useJson) {
5258
- console.log(JSON.stringify({ deleted: true, id }, null, 2));
5259
- } else {
5260
- console.log("\u2713 Goal deleted");
5471
+ goals.command("delete").description(
5472
+ "Delete an objective. Refuses while it still has key results \u2014 deleting an objective deletes them too (KeyResult.goalId cascades), taking their check-in history with them."
5473
+ ).requiredOption("--id <n>", "Objective ID (a number)").option(
5474
+ "--with-key-results",
5475
+ "Delete the objective and cascade-delete its key results (destructive)"
5476
+ ).action(
5477
+ async (options, cmd) => {
5478
+ const globalOpts = cmd.optsWithGlobals();
5479
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
5480
+ try {
5481
+ const id = parseGoalId(options.id);
5482
+ const client = getClient();
5483
+ const goal = await client.goals.get(id);
5484
+ const keyResults = goal.keyResults ?? [];
5485
+ const children = goal.childGoals ?? [];
5486
+ if (keyResults.length > 0 && !options.withKeyResults) {
5487
+ const payload2 = {
5488
+ deleted: false,
5489
+ id,
5490
+ reason: `has ${keyResults.length} key result(s)`,
5491
+ keyResults: keyResults.map((kr) => ({ id: kr.id }))
5492
+ };
5493
+ if (useJson) console.log(JSON.stringify(payload2, null, 2));
5494
+ else {
5495
+ console.error(
5496
+ `Objective #${id} has ${keyResults.length} key result(s). Deleting it deletes them too.
5497
+ Re-run with --with-key-results to go ahead, or move them with \`goals kr update --id <cuid> --goal <other>\`.`
5498
+ );
5499
+ }
5500
+ process.exitCode = 1;
5501
+ return;
5502
+ }
5503
+ await client.goals.delete(id);
5504
+ const payload = {
5505
+ deleted: true,
5506
+ id,
5507
+ keyResultsDeleted: keyResults.map((kr) => ({ id: kr.id })),
5508
+ childGoalsDetached: children.map((c) => ({ id: c.id, title: c.title }))
5509
+ };
5510
+ if (useJson) console.log(JSON.stringify(payload, null, 2));
5511
+ else {
5512
+ console.log("\u2713 Goal deleted");
5513
+ if (keyResults.length) {
5514
+ console.log(` ${keyResults.length} key result(s) deleted with it`);
5515
+ }
5516
+ if (children.length) {
5517
+ console.log(` ${children.length} sub-goal(s) left detached`);
5518
+ }
5519
+ }
5520
+ } catch (error) {
5521
+ handleError(error, useJson);
5261
5522
  }
5262
- } catch (error) {
5263
- handleError(error, useJson);
5264
5523
  }
5265
- });
5524
+ );
5266
5525
  goals.command("periods").description("List the conventional OKR period strings").action(async (_options, cmd) => {
5267
5526
  const globalOpts = cmd.optsWithGlobals();
5268
5527
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);