exponential-cli 1.6.0 → 1.8.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 Command14 } from "commander";
4
+ import { Command as Command19 } from "commander";
5
5
 
6
6
  // src/commands/auth.ts
7
7
  import { Command } from "commander";
@@ -1176,6 +1176,127 @@ Pipeline Stages (${stages.length})`));
1176
1176
  }
1177
1177
  console.log();
1178
1178
  }
1179
+ function outputPageJson(page) {
1180
+ console.log(JSON.stringify(page, null, 2));
1181
+ }
1182
+ function outputPagePretty(page) {
1183
+ console.log(chalk3.gray("\u2500".repeat(50)));
1184
+ console.log(`
1185
+ ${chalk3.bold(page.title)}`);
1186
+ console.log(chalk3.gray(` ID: ${page.id}`));
1187
+ if (page.project) {
1188
+ console.log(` ${chalk3.gray("Project:")} ${page.project.name}`);
1189
+ }
1190
+ if (page.body) {
1191
+ console.log(`
1192
+ ${page.body}
1193
+ `);
1194
+ }
1195
+ }
1196
+ function outputPagesJson(pages) {
1197
+ console.log(JSON.stringify({ pages, total: pages.length }, null, 2));
1198
+ }
1199
+ function outputPagesPretty(pages) {
1200
+ if (pages.length === 0) {
1201
+ console.log(chalk3.gray("No pages found."));
1202
+ return;
1203
+ }
1204
+ console.log(chalk3.bold(`
1205
+ Pages (${pages.length} total)`));
1206
+ console.log(chalk3.gray("\u2500".repeat(50)));
1207
+ for (const p of pages) {
1208
+ console.log(` ${chalk3.bold(p.title)}`);
1209
+ console.log(chalk3.gray(` ID: ${p.id}`));
1210
+ }
1211
+ console.log();
1212
+ }
1213
+ function outputRequirementJson(requirement) {
1214
+ console.log(JSON.stringify(requirement, null, 2));
1215
+ }
1216
+ function outputRequirementPretty(requirement) {
1217
+ const met = requirement.checkedAt ? chalk3.green("[met]") : chalk3.gray("[unmet]");
1218
+ const kind = requirement.kind ? chalk3.gray(` (${requirement.kind})`) : "";
1219
+ console.log(` ${met} ${requirement.statement}${kind}`);
1220
+ console.log(chalk3.gray(` ID: ${requirement.id}`));
1221
+ }
1222
+ function outputRequirementsJson(requirements) {
1223
+ console.log(JSON.stringify({ requirements, total: requirements.length }, null, 2));
1224
+ }
1225
+ function outputRequirementsPretty(requirements) {
1226
+ if (requirements.length === 0) {
1227
+ console.log(chalk3.gray("No requirements found."));
1228
+ return;
1229
+ }
1230
+ const metCount = requirements.filter((r) => r.checkedAt).length;
1231
+ console.log(chalk3.bold(`
1232
+ Requirements (${metCount}/${requirements.length} met)`));
1233
+ console.log(chalk3.gray("\u2500".repeat(50)));
1234
+ for (const r of requirements) {
1235
+ outputRequirementPretty(r);
1236
+ }
1237
+ console.log();
1238
+ }
1239
+ function outputRequirementBatchJson(results) {
1240
+ console.log(JSON.stringify({
1241
+ results,
1242
+ total: results.length,
1243
+ succeeded: results.filter((r) => r.success).length,
1244
+ failed: results.filter((r) => !r.success).length
1245
+ }, null, 2));
1246
+ }
1247
+ function outputRequirementBatchPretty(results) {
1248
+ for (const r of results) {
1249
+ if (r.success) {
1250
+ console.log(` ${chalk3.green("\u2713")} #${r.index}: ${r.requirement?.statement ?? ""}`);
1251
+ } else {
1252
+ console.log(` ${chalk3.red("\u2717")} #${r.index}: ${r.error ?? "failed"}`);
1253
+ }
1254
+ }
1255
+ const ok = results.filter((r) => r.success).length;
1256
+ console.log(chalk3.bold(`
1257
+ ${ok}/${results.length} requirements added`));
1258
+ }
1259
+ function getScopeStatusColor(status) {
1260
+ switch (status) {
1261
+ case "PLANNED":
1262
+ return "blue";
1263
+ case "IN_PROGRESS":
1264
+ return "yellow";
1265
+ case "SHIPPED":
1266
+ return "green";
1267
+ case "DEPRECATED":
1268
+ return "red";
1269
+ default:
1270
+ return "gray";
1271
+ }
1272
+ }
1273
+ function outputScopeJson(scope) {
1274
+ console.log(JSON.stringify(scope, null, 2));
1275
+ }
1276
+ function outputScopePretty(scope) {
1277
+ const badge = chalk3[getScopeStatusColor(scope.status)](`[${scope.status}]`);
1278
+ console.log(` ${badge} ${chalk3.bold(scope.version)}`);
1279
+ console.log(chalk3.gray(` ID: ${scope.id}`));
1280
+ if (scope.description) {
1281
+ console.log(` ${scope.description.substring(0, 120)}${scope.description.length > 120 ? "..." : ""}`);
1282
+ }
1283
+ }
1284
+ function outputScopesJson(scopes) {
1285
+ console.log(JSON.stringify({ scopes, total: scopes.length }, null, 2));
1286
+ }
1287
+ function outputScopesPretty(scopes) {
1288
+ if (scopes.length === 0) {
1289
+ console.log(chalk3.gray("No scopes found."));
1290
+ return;
1291
+ }
1292
+ console.log(chalk3.bold(`
1293
+ Scopes (${scopes.length} total)`));
1294
+ console.log(chalk3.gray("\u2500".repeat(50)));
1295
+ for (const s of scopes) {
1296
+ outputScopePretty(s);
1297
+ }
1298
+ console.log();
1299
+ }
1179
1300
 
1180
1301
  // src/commands/actions.ts
1181
1302
  var VALID_PRIORITIES = [
@@ -1949,7 +2070,7 @@ function createEpicsCommand() {
1949
2070
  }
1950
2071
 
1951
2072
  // src/commands/features.ts
1952
- import { Command as Command7 } from "commander";
2073
+ import { Command as Command10 } from "commander";
1953
2074
 
1954
2075
  // src/commands/stories.ts
1955
2076
  import { Command as Command6 } from "commander";
@@ -2058,12 +2179,325 @@ function createStoriesCommand() {
2058
2179
  return stories;
2059
2180
  }
2060
2181
 
2182
+ // src/commands/scopes.ts
2183
+ import { Command as Command7 } from "commander";
2184
+ var SCOPE_STATUSES = [
2185
+ "PLANNED",
2186
+ "IN_PROGRESS",
2187
+ "SHIPPED",
2188
+ "DEPRECATED"
2189
+ ];
2190
+ function validateScopeStatus(value) {
2191
+ if (!value) return void 0;
2192
+ if (!SCOPE_STATUSES.includes(value)) {
2193
+ throw new Error(
2194
+ `Invalid status "${value}". Valid: ${SCOPE_STATUSES.join(", ")}`
2195
+ );
2196
+ }
2197
+ return value;
2198
+ }
2199
+ function createScopesCommand() {
2200
+ const scopes = new Command7("scopes").description(
2201
+ `Manage a feature's scopes: shippable increments ("V1", "V2"), each with its own lifecycle.`
2202
+ );
2203
+ scopes.command("list").description("List a feature's scopes").requiredOption("--feature <id>", "Feature CUID").action(
2204
+ async (options, cmd) => {
2205
+ const globalOpts = cmd.optsWithGlobals();
2206
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2207
+ try {
2208
+ const client = getClient();
2209
+ const list = await client.scopes.list({ featureId: options.feature });
2210
+ if (useJson) outputScopesJson(list);
2211
+ else outputScopesPretty(list);
2212
+ } catch (error) {
2213
+ handleError(error, useJson);
2214
+ }
2215
+ }
2216
+ );
2217
+ scopes.command("add").description("Add a scope to a feature").requiredOption("--feature <id>", "Feature CUID").requiredOption("--version <label>", 'Short label for the increment, e.g. "V1"').requiredOption("-d, --description <text>", "What this scope delivers (markdown supported)").option("--status <status>", `Status: ${SCOPE_STATUSES.join(", ")} (default PLANNED)`).action(
2218
+ async (options, cmd) => {
2219
+ const globalOpts = cmd.optsWithGlobals();
2220
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2221
+ try {
2222
+ const status = validateScopeStatus(options.status);
2223
+ const client = getClient();
2224
+ const scope = await client.scopes.create({
2225
+ featureId: options.feature,
2226
+ version: options.version,
2227
+ description: options.description,
2228
+ status
2229
+ });
2230
+ if (useJson) outputScopeJson(scope);
2231
+ else {
2232
+ console.log("\n\u2713 Scope added");
2233
+ outputScopePretty(scope);
2234
+ }
2235
+ } catch (error) {
2236
+ handleError(error, useJson);
2237
+ }
2238
+ }
2239
+ );
2240
+ scopes.command("update").description("Update a scope (setting SHIPPED stamps shippedAt and rolls the feature status up)").requiredOption("--id <id>", "Scope CUID").option("--version <label>", "New version label").option("-d, --description <text>", "New description").option("--status <status>", `Status: ${SCOPE_STATUSES.join(", ")}`).action(
2241
+ async (options, cmd) => {
2242
+ const globalOpts = cmd.optsWithGlobals();
2243
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2244
+ try {
2245
+ const status = validateScopeStatus(options.status);
2246
+ const client = getClient();
2247
+ const scope = await client.scopes.update({
2248
+ id: options.id,
2249
+ version: options.version,
2250
+ description: options.description,
2251
+ status
2252
+ });
2253
+ if (useJson) outputScopeJson(scope);
2254
+ else {
2255
+ console.log("\n\u2713 Scope updated");
2256
+ outputScopePretty(scope);
2257
+ }
2258
+ } catch (error) {
2259
+ handleError(error, useJson);
2260
+ }
2261
+ }
2262
+ );
2263
+ scopes.command("rm").description("Delete a scope").requiredOption("--id <id>", "Scope CUID").action(async (options, cmd) => {
2264
+ const globalOpts = cmd.optsWithGlobals();
2265
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2266
+ try {
2267
+ const client = getClient();
2268
+ const result = await client.scopes.delete(options.id);
2269
+ if (useJson) console.log(JSON.stringify(result, null, 2));
2270
+ else console.log("\n\u2713 Scope deleted");
2271
+ } catch (error) {
2272
+ handleError(error, useJson);
2273
+ }
2274
+ });
2275
+ return scopes;
2276
+ }
2277
+
2278
+ // src/commands/requirements.ts
2279
+ import { Command as Command8 } from "commander";
2280
+ var REQUIREMENT_KINDS = [
2281
+ "FUNCTIONAL",
2282
+ "NON_FUNCTIONAL",
2283
+ "CONSTRAINT"
2284
+ ];
2285
+ function validateKind(value) {
2286
+ if (!value) return void 0;
2287
+ if (!REQUIREMENT_KINDS.includes(value)) {
2288
+ throw new Error(
2289
+ `Invalid kind "${value}". Valid: ${REQUIREMENT_KINDS.join(", ")}`
2290
+ );
2291
+ }
2292
+ return value;
2293
+ }
2294
+ async function readStdin2() {
2295
+ const chunks = [];
2296
+ for await (const chunk of process.stdin) {
2297
+ chunks.push(chunk);
2298
+ }
2299
+ return Buffer.concat(chunks).toString("utf-8");
2300
+ }
2301
+ function createRequirementsCommand() {
2302
+ const requirements = new Command8("requirements").description(
2303
+ `Manage a feature's requirements: atomic, testable EARS-style "shall" statements, checkable met/unmet.`
2304
+ );
2305
+ requirements.command("add").description(
2306
+ [
2307
+ "Add requirements to a feature.",
2308
+ "",
2309
+ "Flag mode: pass -s/--statement (plus optional --kind/--scope) to add one requirement.",
2310
+ "Batch mode: pipe a JSON array of { statement, kind?, scopeId? } on stdin",
2311
+ " to add many at once (per-item success/failure is reported)."
2312
+ ].join("\n")
2313
+ ).requiredOption("--feature <id>", "Feature CUID").option("-s, --statement <text>", 'One EARS-style "shall" statement').option("--kind <kind>", `Kind: ${REQUIREMENT_KINDS.join(", ")}`).option("--scope <id>", "Feature scope CUID to pin the requirement to").action(
2314
+ async (options, cmd) => {
2315
+ const globalOpts = cmd.optsWithGlobals();
2316
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2317
+ try {
2318
+ const client = getClient();
2319
+ if (options.statement) {
2320
+ const requirement = await client.requirements.create({
2321
+ featureId: options.feature,
2322
+ statement: options.statement,
2323
+ kind: validateKind(options.kind),
2324
+ scopeId: options.scope
2325
+ });
2326
+ if (useJson) outputRequirementJson(requirement);
2327
+ else {
2328
+ console.log("\n\u2713 Requirement added");
2329
+ outputRequirementPretty(requirement);
2330
+ }
2331
+ return;
2332
+ }
2333
+ if (process.stdin.isTTY) {
2334
+ throw new Error(
2335
+ "Pass -s/--statement, or pipe a JSON array of requirements on stdin."
2336
+ );
2337
+ }
2338
+ const raw = await readStdin2();
2339
+ let batch;
2340
+ try {
2341
+ const parsed = JSON.parse(raw);
2342
+ if (!Array.isArray(parsed)) throw new Error("not an array");
2343
+ batch = parsed;
2344
+ } catch {
2345
+ throw new Error(
2346
+ "stdin must be a JSON array of { statement, kind?, scopeId? } objects."
2347
+ );
2348
+ }
2349
+ const results = [];
2350
+ for (const [index, item] of batch.entries()) {
2351
+ try {
2352
+ if (!item.statement || typeof item.statement !== "string") {
2353
+ throw new Error('missing "statement"');
2354
+ }
2355
+ const input = {
2356
+ featureId: options.feature,
2357
+ statement: item.statement,
2358
+ kind: validateKind(item.kind),
2359
+ scopeId: item.scopeId
2360
+ };
2361
+ const requirement = await client.requirements.create(input);
2362
+ results.push({ index, success: true, requirement });
2363
+ } catch (error) {
2364
+ results.push({
2365
+ index,
2366
+ success: false,
2367
+ error: error instanceof Error ? error.message : String(error)
2368
+ });
2369
+ }
2370
+ }
2371
+ if (useJson) outputRequirementBatchJson(results);
2372
+ else outputRequirementBatchPretty(results);
2373
+ if (results.some((r) => !r.success)) process.exitCode = 1;
2374
+ } catch (error) {
2375
+ handleError(error, useJson);
2376
+ }
2377
+ }
2378
+ );
2379
+ requirements.command("list").description("List a feature's requirements").requiredOption("--feature <id>", "Feature CUID").option("--scope <id>", "Only requirements pinned to this scope").action(
2380
+ async (options, cmd) => {
2381
+ const globalOpts = cmd.optsWithGlobals();
2382
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2383
+ try {
2384
+ const client = getClient();
2385
+ const list = await client.requirements.list({
2386
+ featureId: options.feature,
2387
+ scopeId: options.scope
2388
+ });
2389
+ if (useJson) outputRequirementsJson(list);
2390
+ else outputRequirementsPretty(list);
2391
+ } catch (error) {
2392
+ handleError(error, useJson);
2393
+ }
2394
+ }
2395
+ );
2396
+ requirements.command("check").description("Mark a requirement met (or unmet with --unmet)").requiredOption("--id <id>", "Requirement CUID").option("--unmet", "Mark unmet instead of met").action(
2397
+ async (options, cmd) => {
2398
+ const globalOpts = cmd.optsWithGlobals();
2399
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2400
+ try {
2401
+ const client = getClient();
2402
+ const requirement = await client.requirements.setChecked(
2403
+ options.id,
2404
+ !options.unmet
2405
+ );
2406
+ if (useJson) outputRequirementJson(requirement);
2407
+ else {
2408
+ console.log(`
2409
+ \u2713 Requirement marked ${options.unmet ? "unmet" : "met"}`);
2410
+ outputRequirementPretty(requirement);
2411
+ }
2412
+ } catch (error) {
2413
+ handleError(error, useJson);
2414
+ }
2415
+ }
2416
+ );
2417
+ requirements.command("rm").description("Delete a requirement").requiredOption("--id <id>", "Requirement CUID").action(async (options, cmd) => {
2418
+ const globalOpts = cmd.optsWithGlobals();
2419
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2420
+ try {
2421
+ const client = getClient();
2422
+ const result = await client.requirements.delete(options.id);
2423
+ if (useJson) console.log(JSON.stringify(result, null, 2));
2424
+ else console.log("\n\u2713 Requirement deleted");
2425
+ } catch (error) {
2426
+ handleError(error, useJson);
2427
+ }
2428
+ });
2429
+ return requirements;
2430
+ }
2431
+
2432
+ // src/commands/areas.ts
2433
+ import { Command as Command9 } from "commander";
2434
+ import chalk4 from "chalk";
2435
+ function outputAreasPretty(areas) {
2436
+ if (areas.length === 0) {
2437
+ console.log(chalk4.gray("No areas found."));
2438
+ return;
2439
+ }
2440
+ console.log(chalk4.bold(`
2441
+ Areas (${areas.length} total)`));
2442
+ console.log(chalk4.gray("\u2500".repeat(50)));
2443
+ for (const a of areas) {
2444
+ const count = a._count?.features != null ? chalk4.gray(` (${a._count.features} features)`) : "";
2445
+ console.log(` ${chalk4.bold(a.name)}${count}`);
2446
+ console.log(chalk4.gray(` ID: ${a.id}`));
2447
+ }
2448
+ console.log();
2449
+ }
2450
+ function createAreasCommand() {
2451
+ const areas = new Command9("areas").description(
2452
+ "Manage a product's areas: the buckets features are filed under (a feature has exactly one or none)."
2453
+ );
2454
+ areas.command("list").description("List a product's areas").requiredOption("--product <slug|id>", "Product slug or CUID").option("--workspace <slug|id>", "Workspace (required when --product is a slug)").action(
2455
+ async (options, cmd) => {
2456
+ const globalOpts = cmd.optsWithGlobals();
2457
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2458
+ try {
2459
+ const client = getClient();
2460
+ const workspaceId = await resolveWorkspaceId(client, options.workspace);
2461
+ const productId = await resolveProductId(client, workspaceId, options.product);
2462
+ const list = await client.areas.list({ productId });
2463
+ if (useJson) console.log(JSON.stringify({ areas: list, total: list.length }, null, 2));
2464
+ else outputAreasPretty(list);
2465
+ } catch (error) {
2466
+ handleError(error, useJson);
2467
+ }
2468
+ }
2469
+ );
2470
+ areas.command("create").description("Create an area in a product").requiredOption("--product <slug|id>", "Product slug or CUID").requiredOption("-n, --name <name>", "Area name (unique per product)").option("-d, --description <text>", "What belongs in this area").option("--workspace <slug|id>", "Workspace (required when --product is a slug)").action(
2471
+ async (options, cmd) => {
2472
+ const globalOpts = cmd.optsWithGlobals();
2473
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2474
+ try {
2475
+ const client = getClient();
2476
+ const workspaceId = await resolveWorkspaceId(client, options.workspace);
2477
+ const productId = await resolveProductId(client, workspaceId, options.product);
2478
+ const area = await client.areas.create({
2479
+ productId,
2480
+ name: options.name,
2481
+ description: options.description
2482
+ });
2483
+ if (useJson) console.log(JSON.stringify(area, null, 2));
2484
+ else console.log(`
2485
+ \u2713 Area created: ${area.name} (${area.id})`);
2486
+ } catch (error) {
2487
+ handleError(error, useJson);
2488
+ }
2489
+ }
2490
+ );
2491
+ return areas;
2492
+ }
2493
+
2061
2494
  // src/commands/features.ts
2062
2495
  var FEATURE_STATUSES = [
2063
2496
  "IDEA",
2064
2497
  "DEFINED",
2065
2498
  "IN_PROGRESS",
2066
2499
  "SHIPPED",
2500
+ "DEPRECATED",
2067
2501
  "ARCHIVED"
2068
2502
  ];
2069
2503
  function validateFeatureStatus(value) {
@@ -2076,7 +2510,7 @@ function validateFeatureStatus(value) {
2076
2510
  return value;
2077
2511
  }
2078
2512
  function createFeaturesCommand() {
2079
- const features = new Command7("features").description(
2513
+ const features = new Command10("features").description(
2080
2514
  "Manage features. A feature lives under a product and groups user stories, scopes, and tickets."
2081
2515
  );
2082
2516
  features.command("list").description("List features in a product").requiredOption("--product <slug|id>", "Product slug or CUID").option("--workspace <slug|id>", "Workspace (required when --product is a slug)").option("--status <status>", `Filter by status: ${FEATURE_STATUSES.join(", ")}`).action(
@@ -2112,7 +2546,7 @@ function createFeaturesCommand() {
2112
2546
  handleError(error, useJson);
2113
2547
  }
2114
2548
  });
2115
- features.command("create").description("Create a new feature in a product").requiredOption("--product <slug|id>", "Product slug or CUID").requiredOption("-n, --name <name>", "Feature name").option("-d, --description <text>", "Description (markdown supported)").option("--vision <text>", "Vision / target outcome").option("--status <status>", `Status: ${FEATURE_STATUSES.join(", ")}`).option("--priority <n>", "Priority 0-4 (lower = higher)", parseInt).option("--effort <n>", "Effort estimate", parseFloat).option("--workspace <slug|id>", "Workspace (required when --product is a slug)").action(
2549
+ features.command("create").description("Create a new feature in a product").requiredOption("--product <slug|id>", "Product slug or CUID").requiredOption("-n, --name <name>", "Feature name").option("-d, --description <text>", "Description (markdown supported)").option("--vision <text>", "Vision / target outcome").option("--status <status>", `Status: ${FEATURE_STATUSES.join(", ")}`).option("--priority <n>", "Priority 0-4 (lower = higher)", parseInt).option("--effort <n>", "Effort estimate", parseFloat).option("--area <id>", "Area CUID (per-product bucket) to file the feature under").option("--workspace <slug|id>", "Workspace (required when --product is a slug)").action(
2116
2550
  async (options, cmd) => {
2117
2551
  const globalOpts = cmd.optsWithGlobals();
2118
2552
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -2132,7 +2566,8 @@ function createFeaturesCommand() {
2132
2566
  vision: options.vision,
2133
2567
  status,
2134
2568
  priority: options.priority,
2135
- effort: options.effort
2569
+ effort: options.effort,
2570
+ areaId: options.area
2136
2571
  });
2137
2572
  if (useJson) outputFeatureJson(feature);
2138
2573
  else {
@@ -2144,7 +2579,7 @@ function createFeaturesCommand() {
2144
2579
  }
2145
2580
  }
2146
2581
  );
2147
- 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).action(
2582
+ 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(
2148
2583
  async (options, cmd) => {
2149
2584
  const globalOpts = cmd.optsWithGlobals();
2150
2585
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -2158,7 +2593,8 @@ function createFeaturesCommand() {
2158
2593
  vision: options.vision,
2159
2594
  status,
2160
2595
  priority: options.priority,
2161
- effort: options.effort
2596
+ effort: options.effort,
2597
+ areaId: options.area === "none" ? null : options.area
2162
2598
  });
2163
2599
  if (useJson) outputFeatureJson(feature);
2164
2600
  else {
@@ -2170,13 +2606,51 @@ function createFeaturesCommand() {
2170
2606
  }
2171
2607
  }
2172
2608
  );
2609
+ features.command("link-page").description("Link a Knowledge page (PRD, spec, research) to a feature").requiredOption("--feature <id>", "Feature CUID").requiredOption("--page <id>", "Page CUID").option("--scope <id>", "Feature scope CUID to pin the page to").action(
2610
+ async (options, cmd) => {
2611
+ const globalOpts = cmd.optsWithGlobals();
2612
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2613
+ try {
2614
+ const client = getClient();
2615
+ const link = await client.features.linkPage({
2616
+ featureId: options.feature,
2617
+ pageId: options.page,
2618
+ scopeId: options.scope
2619
+ });
2620
+ if (useJson) console.log(JSON.stringify(link, null, 2));
2621
+ else console.log("\n\u2713 Page linked");
2622
+ } catch (error) {
2623
+ handleError(error, useJson);
2624
+ }
2625
+ }
2626
+ );
2627
+ features.command("unlink-page").description("Unlink a Knowledge page from a feature").requiredOption("--feature <id>", "Feature CUID").requiredOption("--page <id>", "Page CUID").action(
2628
+ async (options, cmd) => {
2629
+ const globalOpts = cmd.optsWithGlobals();
2630
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2631
+ try {
2632
+ const client = getClient();
2633
+ const result = await client.features.unlinkPage(
2634
+ options.feature,
2635
+ options.page
2636
+ );
2637
+ if (useJson) console.log(JSON.stringify(result, null, 2));
2638
+ else console.log("\n\u2713 Page unlinked");
2639
+ } catch (error) {
2640
+ handleError(error, useJson);
2641
+ }
2642
+ }
2643
+ );
2173
2644
  features.addCommand(createStoriesCommand());
2645
+ features.addCommand(createScopesCommand());
2646
+ features.addCommand(createRequirementsCommand());
2647
+ features.addCommand(createAreasCommand());
2174
2648
  return features;
2175
2649
  }
2176
2650
 
2177
2651
  // src/commands/labels.ts
2178
- import { Command as Command8 } from "commander";
2179
- import chalk4 from "chalk";
2652
+ import { Command as Command11 } from "commander";
2653
+ import chalk5 from "chalk";
2180
2654
  var TRIAGE_LABELS = [
2181
2655
  { slug: "needs-triage", name: "needs-triage", color: "avatar-orange" },
2182
2656
  { slug: "needs-info", name: "needs-info", color: "avatar-yellow" },
@@ -2190,22 +2664,22 @@ function looksLikeCuid(value) {
2190
2664
  }
2191
2665
  function outputTagsPretty(tags) {
2192
2666
  if (tags.length === 0) {
2193
- console.log(chalk4.dim("No labels found."));
2667
+ console.log(chalk5.dim("No labels found."));
2194
2668
  return;
2195
2669
  }
2196
2670
  console.log();
2197
2671
  for (const tag of tags) {
2198
- const scope = tag.workspaceId === null ? chalk4.cyan("global") : chalk4.magenta("workspace");
2199
- const category = tag.category === null ? chalk4.dim("(label)") : chalk4.dim(`(${tag.category})`);
2672
+ const scope = tag.workspaceId === null ? chalk5.cyan("global") : chalk5.magenta("workspace");
2673
+ const category = tag.category === null ? chalk5.dim("(label)") : chalk5.dim(`(${tag.category})`);
2200
2674
  console.log(
2201
- ` ${chalk4.bold(tag.slug)} ${tag.name} ${chalk4.gray(tag.color)} ${scope} ${category}`
2675
+ ` ${chalk5.bold(tag.slug)} ${tag.name} ${chalk5.gray(tag.color)} ${scope} ${category}`
2202
2676
  );
2203
2677
  }
2204
2678
  console.log();
2205
2679
  }
2206
2680
  function outputTagPretty(tag, prefix) {
2207
2681
  if (prefix) console.log(prefix);
2208
- console.log(` ${chalk4.bold(tag.slug)} ${tag.name} ${chalk4.gray(tag.color)}`);
2682
+ console.log(` ${chalk5.bold(tag.slug)} ${tag.name} ${chalk5.gray(tag.color)}`);
2209
2683
  }
2210
2684
  async function resolveLabelIds(client, workspaceId, values) {
2211
2685
  if (values.length === 0) return [];
@@ -2229,7 +2703,7 @@ async function resolveLabelIds(client, workspaceId, values) {
2229
2703
  return [...byId, ...resolvedFromSlugs];
2230
2704
  }
2231
2705
  function createLabelsCommand() {
2232
- const labels = new Command8("labels").description(
2706
+ const labels = new Command11("labels").description(
2233
2707
  "Manage labels (orthogonal tags applied to actions, tickets, features, and epics)."
2234
2708
  );
2235
2709
  labels.command("list").description("List labels visible in a workspace").option("--workspace <slug|id>", "Workspace slug or CUID").option(
@@ -2277,7 +2751,7 @@ function createLabelsCommand() {
2277
2751
  if (useJson) {
2278
2752
  console.log(JSON.stringify(tag, null, 2));
2279
2753
  } else {
2280
- outputTagPretty(tag, chalk4.green("\n\u2713 Label created"));
2754
+ outputTagPretty(tag, chalk5.green("\n\u2713 Label created"));
2281
2755
  }
2282
2756
  } catch (error) {
2283
2757
  handleError(error, useJson);
@@ -2315,10 +2789,10 @@ function createLabelsCommand() {
2315
2789
  console.log(JSON.stringify(results, null, 2));
2316
2790
  } else {
2317
2791
  console.log();
2318
- console.log(chalk4.bold("Triage labels:"));
2792
+ console.log(chalk5.bold("Triage labels:"));
2319
2793
  for (const r of results) {
2320
- const tag = chalk4.bold(r.slug);
2321
- const status = r.status === "created" ? chalk4.green("created") : chalk4.dim("existed");
2794
+ const tag = chalk5.bold(r.slug);
2795
+ const status = r.status === "created" ? chalk5.green("created") : chalk5.dim("existed");
2322
2796
  console.log(` ${tag} ${status}`);
2323
2797
  }
2324
2798
  console.log();
@@ -2332,7 +2806,7 @@ function createLabelsCommand() {
2332
2806
  }
2333
2807
 
2334
2808
  // src/commands/organizations.ts
2335
- import { Command as Command9 } from "commander";
2809
+ import { Command as Command12 } from "commander";
2336
2810
  var VALID_SIZES = [
2337
2811
  "1-10",
2338
2812
  "11-50",
@@ -2342,7 +2816,7 @@ var VALID_SIZES = [
2342
2816
  "1000+"
2343
2817
  ];
2344
2818
  function createOrganizationsCommand() {
2345
- const organizations = new Command9("organizations").alias("orgs").description("Manage CRM organizations");
2819
+ const organizations = new Command12("organizations").alias("orgs").description("Manage CRM organizations");
2346
2820
  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) => {
2347
2821
  const globalOpts = cmd.optsWithGlobals();
2348
2822
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -2409,10 +2883,101 @@ function createOrganizationsCommand() {
2409
2883
  return organizations;
2410
2884
  }
2411
2885
 
2886
+ // src/commands/pages.ts
2887
+ import { readFileSync } from "fs";
2888
+ import { Command as Command13 } from "commander";
2889
+ function readBody(body, bodyFile) {
2890
+ if (body !== void 0) return body;
2891
+ if (bodyFile === void 0) return void 0;
2892
+ if (bodyFile === "-") return readFileSync(0, "utf-8");
2893
+ return readFileSync(bodyFile, "utf-8");
2894
+ }
2895
+ function createPagesCommand() {
2896
+ const pages = new Command13("pages").description(
2897
+ 'Manage Knowledge pages (PRDs, research, technical specs). Link one to a feature with "features link-page".'
2898
+ );
2899
+ 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(
2900
+ async (options, cmd) => {
2901
+ const globalOpts = cmd.optsWithGlobals();
2902
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2903
+ try {
2904
+ const client = getClient();
2905
+ const workspaceId = await resolveWorkspaceId(client, options.workspace);
2906
+ const list = await client.pages.list({
2907
+ workspaceId,
2908
+ search: options.search
2909
+ });
2910
+ if (useJson) outputPagesJson(list);
2911
+ else outputPagesPretty(list);
2912
+ } catch (error) {
2913
+ handleError(error, useJson);
2914
+ }
2915
+ }
2916
+ );
2917
+ pages.command("get <id>").description("Get a page by CUID (body is Markdown)").action(async (id, _options, cmd) => {
2918
+ const globalOpts = cmd.optsWithGlobals();
2919
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2920
+ try {
2921
+ const client = getClient();
2922
+ const page = await client.pages.get(id);
2923
+ if (useJson) outputPageJson(page);
2924
+ else outputPagePretty(page);
2925
+ } catch (error) {
2926
+ handleError(error, useJson);
2927
+ }
2928
+ });
2929
+ pages.command("create").description("Create a page. Body: -b text, --body-file <path>, or --body-file - for stdin.").option("--workspace <slug|id>", "Workspace (defaults to the configured default)").requiredOption("-t, --title <title>", "Page title").option("-b, --body <markdown>", "Markdown body").option("--body-file <path>", 'Read the Markdown body from a file ("-" = stdin)').action(
2930
+ async (options, cmd) => {
2931
+ const globalOpts = cmd.optsWithGlobals();
2932
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2933
+ try {
2934
+ const client = getClient();
2935
+ const workspaceId = await resolveWorkspaceId(client, options.workspace);
2936
+ const page = await client.pages.create({
2937
+ workspaceId,
2938
+ title: options.title,
2939
+ body: readBody(options.body, options.bodyFile)
2940
+ });
2941
+ if (useJson) outputPageJson(page);
2942
+ else {
2943
+ console.log("\n\u2713 Page created");
2944
+ outputPagePretty(page);
2945
+ }
2946
+ } catch (error) {
2947
+ handleError(error, useJson);
2948
+ }
2949
+ }
2950
+ );
2951
+ pages.command("update").description(
2952
+ "Update a page. Sending a body is a Markdown-source write: the Markdown becomes canonical."
2953
+ ).requiredOption("--id <id>", "Page CUID").option("-t, --title <title>", "New title").option("-b, --body <markdown>", "New Markdown body (replaces the whole body)").option("--body-file <path>", 'Read the new Markdown body from a file ("-" = stdin)').action(
2954
+ async (options, cmd) => {
2955
+ const globalOpts = cmd.optsWithGlobals();
2956
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2957
+ try {
2958
+ const client = getClient();
2959
+ const page = await client.pages.update({
2960
+ id: options.id,
2961
+ title: options.title,
2962
+ body: readBody(options.body, options.bodyFile)
2963
+ });
2964
+ if (useJson) outputPageJson(page);
2965
+ else {
2966
+ console.log("\n\u2713 Page updated");
2967
+ outputPagePretty(page);
2968
+ }
2969
+ } catch (error) {
2970
+ handleError(error, useJson);
2971
+ }
2972
+ }
2973
+ );
2974
+ return pages;
2975
+ }
2976
+
2412
2977
  // src/commands/products.ts
2413
- import { Command as Command10 } from "commander";
2978
+ import { Command as Command14 } from "commander";
2414
2979
  function createProductsCommand() {
2415
- const products = new Command10("products").description(
2980
+ const products = new Command14("products").description(
2416
2981
  "Manage products. A workspace contains products; a product holds features and tickets."
2417
2982
  );
2418
2983
  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) => {
@@ -2475,9 +3040,9 @@ function createProductsCommand() {
2475
3040
  }
2476
3041
 
2477
3042
  // src/commands/projects.ts
2478
- import { Command as Command11 } from "commander";
3043
+ import { Command as Command15 } from "commander";
2479
3044
  function createProjectsCommand() {
2480
- const projects = new Command11("projects").description("Manage projects");
3045
+ const projects = new Command15("projects").description("Manage projects");
2481
3046
  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) => {
2482
3047
  const globalOpts = cmd.optsWithGlobals();
2483
3048
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -2499,8 +3064,86 @@ function createProjectsCommand() {
2499
3064
  return projects;
2500
3065
  }
2501
3066
 
3067
+ // src/commands/search.ts
3068
+ import { Command as Command16 } from "commander";
3069
+ import chalk6 from "chalk";
3070
+ var TYPE_LABELS = {
3071
+ workspace: "Workspaces",
3072
+ project: "Projects",
3073
+ action: "Actions",
3074
+ goal: "Goals",
3075
+ keyResult: "Key Results",
3076
+ outcome: "Outcomes",
3077
+ ticket: "Tickets",
3078
+ feature: "Features",
3079
+ epic: "Epics",
3080
+ page: "Pages",
3081
+ meeting: "Meetings",
3082
+ contact: "Contacts",
3083
+ organization: "Organizations",
3084
+ product: "Products"
3085
+ };
3086
+ function createSearchCommand() {
3087
+ const search = new Command16("search").description(
3088
+ "Global search across projects, actions, goals, workspaces and more \u2014 same coverage as the app's Cmd+K palette"
3089
+ ).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) => {
3090
+ const globalOpts = cmd.optsWithGlobals();
3091
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
3092
+ try {
3093
+ const client = getClient();
3094
+ let workspaceId;
3095
+ if (options.workspace) {
3096
+ const workspaces = await client.workspaces.list();
3097
+ const match = workspaces.find(
3098
+ (w) => w.slug === options.workspace || w.id === options.workspace
3099
+ );
3100
+ if (!match) {
3101
+ console.error(chalk6.red(`Workspace "${options.workspace}" not found.`));
3102
+ console.log(chalk6.gray("Available workspaces:"));
3103
+ workspaces.forEach((w) => {
3104
+ console.log(chalk6.gray(` - ${w.slug} (${w.name})`));
3105
+ });
3106
+ process.exit(1);
3107
+ }
3108
+ workspaceId = match.id;
3109
+ }
3110
+ const limit = options.limit ? Number(options.limit) : void 0;
3111
+ const result = await client.search.global({ query, workspaceId, limit });
3112
+ if (useJson) {
3113
+ console.log(JSON.stringify(result, null, 2));
3114
+ return;
3115
+ }
3116
+ if (result.results.length === 0) {
3117
+ console.log(chalk6.gray(`No matches for "${result.query}".`));
3118
+ return;
3119
+ }
3120
+ const byType = /* @__PURE__ */ new Map();
3121
+ for (const r of result.results) {
3122
+ const bucket = byType.get(r.type) ?? [];
3123
+ bucket.push(r);
3124
+ byType.set(r.type, bucket);
3125
+ }
3126
+ for (const [type, rows] of byType) {
3127
+ console.log(chalk6.bold(`
3128
+ ${TYPE_LABELS[type] ?? type}`));
3129
+ for (const row of rows) {
3130
+ const parts = [chalk6.white(row.title)];
3131
+ if (row.subtitle) parts.push(chalk6.gray(row.subtitle));
3132
+ if (row.workspace) parts.push(chalk6.cyan(`@${row.workspace.slug}`));
3133
+ console.log(` ${parts.join(chalk6.gray(" \xB7 "))}`);
3134
+ console.log(chalk6.gray(` ${row.id}${row.url ? ` ${row.url}` : ""}`));
3135
+ }
3136
+ }
3137
+ console.log();
3138
+ } catch (error) {
3139
+ handleError(error, useJson);
3140
+ }
3141
+ });
3142
+ return search;
3143
+ }
3144
+
2502
3145
  // src/commands/tickets.ts
2503
- import { Command as Command12 } from "commander";
3146
+ import { Command as Command17 } from "commander";
2504
3147
  function collectRepeatable(value, previous) {
2505
3148
  return [...previous, value];
2506
3149
  }
@@ -2543,7 +3186,7 @@ function validateTicketType(value) {
2543
3186
  return value;
2544
3187
  }
2545
3188
  function createTicketsCommand() {
2546
- const tickets = new Command12("tickets").description(
3189
+ const tickets = new Command17("tickets").description(
2547
3190
  "Manage product backlog tickets. Tickets live under a product, link to features/epics, depend on each other, and can be implemented via actions."
2548
3191
  );
2549
3192
  tickets.command("list").description(
@@ -2646,7 +3289,7 @@ function createTicketsCommand() {
2646
3289
  handleError(error, useJson);
2647
3290
  }
2648
3291
  });
2649
- 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("--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(
3292
+ 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(
2650
3293
  "--label <slug-or-id>",
2651
3294
  "Label to attach (repeatable)",
2652
3295
  collectRepeatable,
@@ -2674,6 +3317,7 @@ function createTicketsCommand() {
2674
3317
  priority: options.priority,
2675
3318
  points: options.points,
2676
3319
  featureId: options.feature,
3320
+ scopeId: options.scope,
2677
3321
  epicId: options.epic,
2678
3322
  assigneeId: options.assignee,
2679
3323
  branchName: options.branch,
@@ -2856,7 +3500,7 @@ function createTicketsCommand() {
2856
3500
  handleError(error, useJson);
2857
3501
  }
2858
3502
  });
2859
- const comment = new Command12("comment").description("Manage comments on a ticket");
3503
+ const comment = new Command17("comment").description("Manage comments on a ticket");
2860
3504
  comment.command("add").description("Add a comment to a ticket").requiredOption("--id <id>", "Ticket CUID").requiredOption("-m, --message <text>", "Comment content (markdown supported)").action(async (options, cmd) => {
2861
3505
  const globalOpts = cmd.optsWithGlobals();
2862
3506
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -2895,10 +3539,10 @@ function createTicketsCommand() {
2895
3539
  }
2896
3540
 
2897
3541
  // src/commands/workspaces.ts
2898
- import { Command as Command13 } from "commander";
2899
- import chalk5 from "chalk";
3542
+ import { Command as Command18 } from "commander";
3543
+ import chalk7 from "chalk";
2900
3544
  function createWorkspacesCommand() {
2901
- const workspaces = new Command13("workspaces").description("Manage workspaces");
3545
+ const workspaces = new Command18("workspaces").description("Manage workspaces");
2902
3546
  workspaces.command("list").description("List all workspaces").action(async (_options, cmd) => {
2903
3547
  const globalOpts = cmd.optsWithGlobals();
2904
3548
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -2911,7 +3555,7 @@ function createWorkspacesCommand() {
2911
3555
  const config = getConfig();
2912
3556
  outputWorkspacesPretty(workspaces2);
2913
3557
  if (config.defaultWorkspaceSlug) {
2914
- console.log(chalk5.gray(`Default workspace: ${config.defaultWorkspaceSlug}`));
3558
+ console.log(chalk7.gray(`Default workspace: ${config.defaultWorkspaceSlug}`));
2915
3559
  }
2916
3560
  }
2917
3561
  } catch (error) {
@@ -2924,10 +3568,10 @@ function createWorkspacesCommand() {
2924
3568
  const workspaces2 = await client.workspaces.list();
2925
3569
  const workspace = workspaces2.find((w) => w.slug === slug);
2926
3570
  if (!workspace) {
2927
- console.error(chalk5.red(`Workspace with slug "${slug}" not found.`));
2928
- console.log(chalk5.gray("Available workspaces:"));
3571
+ console.error(chalk7.red(`Workspace with slug "${slug}" not found.`));
3572
+ console.log(chalk7.gray("Available workspaces:"));
2929
3573
  workspaces2.forEach((w) => {
2930
- console.log(chalk5.gray(` - ${w.slug} (${w.name})`));
3574
+ console.log(chalk7.gray(` - ${w.slug} (${w.name})`));
2931
3575
  });
2932
3576
  process.exit(1);
2933
3577
  }
@@ -2935,7 +3579,7 @@ function createWorkspacesCommand() {
2935
3579
  defaultWorkspaceId: workspace.id,
2936
3580
  defaultWorkspaceSlug: workspace.slug
2937
3581
  });
2938
- console.log(chalk5.green(`Default workspace set to: ${workspace.name} (${workspace.slug})`));
3582
+ console.log(chalk7.green(`Default workspace set to: ${workspace.name} (${workspace.slug})`));
2939
3583
  } catch (error) {
2940
3584
  handleError(error);
2941
3585
  }
@@ -2944,7 +3588,7 @@ function createWorkspacesCommand() {
2944
3588
  }
2945
3589
 
2946
3590
  // src/index.ts
2947
- var program = new Command14();
3591
+ var program = new Command19();
2948
3592
  program.name("exponential").description(
2949
3593
  [
2950
3594
  "CLI to interact with Exponential productivity app.",
@@ -2962,8 +3606,10 @@ program.addCommand(createEpicsCommand());
2962
3606
  program.addCommand(createFeaturesCommand());
2963
3607
  program.addCommand(createLabelsCommand());
2964
3608
  program.addCommand(createOrganizationsCommand());
3609
+ program.addCommand(createPagesCommand());
2965
3610
  program.addCommand(createProductsCommand());
2966
3611
  program.addCommand(createProjectsCommand());
3612
+ program.addCommand(createSearchCommand());
2967
3613
  program.addCommand(createTicketsCommand());
2968
3614
  program.addCommand(createWorkspacesCommand());
2969
3615
  program.parse();