exponential-cli 1.4.0 → 1.6.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 Command13 } from "commander";
4
+ import { Command as Command14 } from "commander";
5
5
 
6
6
  // src/commands/auth.ts
7
7
  import { Command } from "commander";
@@ -499,6 +499,60 @@ function outputInteractionPretty(interaction) {
499
499
  if (interaction.notes) console.log(` ${chalk3.gray("Notes:")} ${interaction.notes}`);
500
500
  console.log();
501
501
  }
502
+ function transformOrganization(org) {
503
+ return {
504
+ id: org.id,
505
+ workspaceId: org.workspaceId,
506
+ name: org.name,
507
+ websiteUrl: org.websiteUrl,
508
+ logoUrl: org.logoUrl,
509
+ description: org.description,
510
+ industry: org.industry,
511
+ size: org.size,
512
+ contactCount: org._count?.contacts ?? null,
513
+ createdAt: new Date(org.createdAt).toISOString(),
514
+ updatedAt: new Date(org.updatedAt).toISOString()
515
+ };
516
+ }
517
+ function outputOrganizationJson(org) {
518
+ console.log(JSON.stringify(transformOrganization(org), null, 2));
519
+ }
520
+ function outputOrganizationPretty(org) {
521
+ console.log(chalk3.gray("\u2500".repeat(50)));
522
+ console.log(`
523
+ ${chalk3.bold(org.name)}`);
524
+ console.log(chalk3.gray(` ID: ${org.id}`));
525
+ if (org.industry) console.log(` ${chalk3.magenta("Industry:")} ${org.industry}`);
526
+ if (org.size) console.log(` ${chalk3.cyan("Size:")} ${org.size}`);
527
+ if (org.websiteUrl) console.log(` ${chalk3.cyan("Website:")} ${org.websiteUrl}`);
528
+ if (org.description) console.log(` ${chalk3.gray("About:")} ${org.description.substring(0, 120)}${org.description.length > 120 ? "..." : ""}`);
529
+ if (org._count?.contacts != null) console.log(` ${chalk3.green("Contacts:")} ${org._count.contacts}`);
530
+ console.log();
531
+ }
532
+ function outputOrganizationsJson(organizations, nextCursor) {
533
+ console.log(JSON.stringify({
534
+ organizations: organizations.map(transformOrganization),
535
+ total: organizations.length,
536
+ nextCursor: nextCursor ?? null
537
+ }, null, 2));
538
+ }
539
+ function outputOrganizationsPretty(organizations) {
540
+ if (organizations.length === 0) {
541
+ console.log(chalk3.gray("No organizations found."));
542
+ return;
543
+ }
544
+ console.log(chalk3.bold(`
545
+ Organizations (${organizations.length} total)`));
546
+ console.log(chalk3.gray("\u2500".repeat(50)));
547
+ for (const org of organizations) {
548
+ console.log(`
549
+ ${chalk3.bold(org.name)}`);
550
+ console.log(chalk3.gray(` ID: ${org.id}`));
551
+ if (org.industry) console.log(` ${chalk3.magenta("Industry:")} ${org.industry}`);
552
+ if (org._count?.contacts != null) console.log(` ${chalk3.green("Contacts:")} ${org._count.contacts}`);
553
+ }
554
+ console.log();
555
+ }
502
556
  function outputDealJson(deal) {
503
557
  console.log(JSON.stringify(transformDeal(deal), null, 2));
504
558
  }
@@ -1417,7 +1471,7 @@ function createContactsCommand() {
1417
1471
  handleError(error, useJson);
1418
1472
  }
1419
1473
  });
1420
- contacts.command("create").description("Create a new contact").requiredOption("--workspace <id>", "Workspace ID").option("--first-name <name>", "First name").option("--last-name <name>", "Last name").option("--email <email>", "Email address").option("--phone <phone>", "Phone number").option("--linkedin <url>", "LinkedIn URL").option("--telegram <handle>", "Telegram handle").option("--twitter <handle>", "Twitter/X handle").option("--github <handle>", "GitHub username").option("--bluesky <handle>", "Bluesky handle").option("--about <text>", "About / bio").option("--profile-type <type>", "Profile type").option("--skills <skills>", "Skills (comma-separated)").option("--tags <tags>", "Tags (comma-separated)").option("--organization <id>", "Organization ID").action(async (options, cmd) => {
1474
+ contacts.command("create").description("Create a new contact").requiredOption("--workspace <id>", "Workspace ID").option("--first-name <name>", "First name").option("--last-name <name>", "Last name").option("--email <email>", "Email address").option("--phone <phone>", "Phone number").option("--linkedin <url>", "LinkedIn URL").option("--telegram <handle>", "Telegram handle").option("--twitter <handle>", "Twitter/X handle").option("--github <handle>", "GitHub username").option("--bluesky <handle>", "Bluesky handle").option("--about <text>", "About / bio").option("--profile-type <type>", "Profile type").option("--skills <skills>", "Skills (comma-separated)").option("--tags <tags>", "Tags (comma-separated)").option("--organization <id>", "Organization ID").option("--organization-name <name>", "Organization name (find-or-create; ignored if --organization is set)").action(async (options, cmd) => {
1421
1475
  const globalOpts = cmd.optsWithGlobals();
1422
1476
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
1423
1477
  try {
@@ -1437,7 +1491,8 @@ function createContactsCommand() {
1437
1491
  profileType: options.profileType,
1438
1492
  skills: options.skills?.split(",").map((s) => s.trim()),
1439
1493
  tags: options.tags?.split(",").map((t) => t.trim()),
1440
- organizationId: options.organization
1494
+ organizationId: options.organization,
1495
+ organizationName: options.organizationName
1441
1496
  });
1442
1497
  if (useJson) {
1443
1498
  outputContactJson(contact);
@@ -1449,7 +1504,7 @@ function createContactsCommand() {
1449
1504
  handleError(error, useJson);
1450
1505
  }
1451
1506
  });
1452
- contacts.command("update").description("Update an existing contact").requiredOption("--id <id>", "Contact ID").option("--first-name <name>", "First name").option("--last-name <name>", "Last name").option("--email <email>", 'Email (use "null" to clear)').option("--phone <phone>", 'Phone (use "null" to clear)').option("--linkedin <url>", 'LinkedIn URL (use "null" to clear)').option("--telegram <handle>", 'Telegram handle (use "null" to clear)').option("--twitter <handle>", 'Twitter/X handle (use "null" to clear)').option("--github <handle>", 'GitHub username (use "null" to clear)').option("--bluesky <handle>", 'Bluesky handle (use "null" to clear)').option("--about <text>", "About / bio").option("--profile-type <type>", "Profile type").option("--skills <skills>", "Skills (comma-separated)").option("--tags <tags>", "Tags (comma-separated)").option("--organization <id>", 'Organization ID (use "null" to clear)').action(async (options, cmd) => {
1507
+ contacts.command("update").description("Update an existing contact").requiredOption("--id <id>", "Contact ID").option("--first-name <name>", "First name").option("--last-name <name>", "Last name").option("--email <email>", 'Email (use "null" to clear)').option("--phone <phone>", 'Phone (use "null" to clear)').option("--linkedin <url>", 'LinkedIn URL (use "null" to clear)').option("--telegram <handle>", 'Telegram handle (use "null" to clear)').option("--twitter <handle>", 'Twitter/X handle (use "null" to clear)').option("--github <handle>", 'GitHub username (use "null" to clear)').option("--bluesky <handle>", 'Bluesky handle (use "null" to clear)').option("--about <text>", "About / bio").option("--profile-type <type>", "Profile type").option("--skills <skills>", "Skills (comma-separated)").option("--tags <tags>", "Tags (comma-separated)").option("--organization <id>", 'Organization ID (use "null" to clear)').option("--organization-name <name>", "Organization name (find-or-create; ignored if --organization is set)").action(async (options, cmd) => {
1453
1508
  const globalOpts = cmd.optsWithGlobals();
1454
1509
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
1455
1510
  const nullable = (v) => v === "null" ? null : v;
@@ -1470,7 +1525,8 @@ function createContactsCommand() {
1470
1525
  profileType: options.profileType,
1471
1526
  skills: options.skills?.split(",").map((s) => s.trim()),
1472
1527
  tags: options.tags?.split(",").map((t) => t.trim()),
1473
- organizationId: nullable(options.organization)
1528
+ organizationId: nullable(options.organization),
1529
+ organizationName: options.organizationName
1474
1530
  });
1475
1531
  if (useJson) {
1476
1532
  outputContactJson(contact);
@@ -1526,6 +1582,28 @@ function createContactsCommand() {
1526
1582
  handleError(error, useJson);
1527
1583
  }
1528
1584
  });
1585
+ contacts.command("enrich").description("Queue a web-search enrichment job for an existing contact (runs regardless of the workspace auto-enrich setting)").argument("<id>", "Contact ID").action(async (id, _options, cmd) => {
1586
+ const globalOpts = cmd.optsWithGlobals();
1587
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
1588
+ try {
1589
+ const client = getClient();
1590
+ const result = await client.contacts.enrich(id);
1591
+ if (useJson) {
1592
+ console.log(JSON.stringify(result, null, 2));
1593
+ } else if (result.enqueued) {
1594
+ console.log(`
1595
+ \u2713 Enrichment queued for contact ${id} (job ${result.enrichmentId})`);
1596
+ } else if (result.reason === "already-in-flight") {
1597
+ console.log(`
1598
+ \u2022 Enrichment already in progress for contact ${id} (job ${result.enrichmentId})`);
1599
+ } else {
1600
+ console.log(`
1601
+ \u2022 Not queued for contact ${id}${result.reason ? ` (${result.reason})` : ""}`);
1602
+ }
1603
+ } catch (error) {
1604
+ handleError(error, useJson);
1605
+ }
1606
+ });
1529
1607
  return contacts;
1530
1608
  }
1531
1609
 
@@ -2253,10 +2331,88 @@ function createLabelsCommand() {
2253
2331
  return labels;
2254
2332
  }
2255
2333
 
2256
- // src/commands/products.ts
2334
+ // src/commands/organizations.ts
2257
2335
  import { Command as Command9 } from "commander";
2336
+ var VALID_SIZES = [
2337
+ "1-10",
2338
+ "11-50",
2339
+ "51-200",
2340
+ "201-500",
2341
+ "501-1000",
2342
+ "1000+"
2343
+ ];
2344
+ function createOrganizationsCommand() {
2345
+ const organizations = new Command9("organizations").alias("orgs").description("Manage CRM organizations");
2346
+ 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
+ const globalOpts = cmd.optsWithGlobals();
2348
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2349
+ try {
2350
+ const client = getClient();
2351
+ const result = await client.organizations.list({
2352
+ workspaceId: options.workspace,
2353
+ search: options.search,
2354
+ industry: options.industry,
2355
+ limit: options.limit,
2356
+ cursor: options.cursor
2357
+ });
2358
+ if (useJson) {
2359
+ outputOrganizationsJson(result.organizations, result.nextCursor);
2360
+ } else {
2361
+ outputOrganizationsPretty(result.organizations);
2362
+ }
2363
+ } catch (error) {
2364
+ handleError(error, useJson);
2365
+ }
2366
+ });
2367
+ organizations.command("get").description("Get an organization by ID").argument("<id>", "Organization ID").action(async (id, _options, cmd) => {
2368
+ const globalOpts = cmd.optsWithGlobals();
2369
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2370
+ try {
2371
+ const client = getClient();
2372
+ const org = await client.organizations.get(id);
2373
+ if (useJson) {
2374
+ outputOrganizationJson(org);
2375
+ } else {
2376
+ outputOrganizationPretty(org);
2377
+ }
2378
+ } catch (error) {
2379
+ handleError(error, useJson);
2380
+ }
2381
+ });
2382
+ organizations.command("create").description("Create a new organization").requiredOption("--workspace <id>", "Workspace ID").requiredOption("--name <name>", "Organization name").option("--website-url <url>", "Website URL").option("--logo-url <url>", "Logo URL").option("--description <text>", "Description").option("--industry <industry>", "Industry").option("--size <size>", `Company size (${VALID_SIZES.join(", ")})`).action(async (options, cmd) => {
2383
+ const globalOpts = cmd.optsWithGlobals();
2384
+ const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
2385
+ try {
2386
+ if (options.size && !VALID_SIZES.includes(options.size)) {
2387
+ throw new Error(`Invalid size "${options.size}". Valid: ${VALID_SIZES.join(", ")}`);
2388
+ }
2389
+ const client = getClient();
2390
+ const org = await client.organizations.create({
2391
+ workspaceId: options.workspace,
2392
+ name: options.name,
2393
+ websiteUrl: options.websiteUrl,
2394
+ logoUrl: options.logoUrl,
2395
+ description: options.description,
2396
+ industry: options.industry,
2397
+ size: options.size
2398
+ });
2399
+ if (useJson) {
2400
+ outputOrganizationJson(org);
2401
+ } else {
2402
+ console.log("\n\u2713 Organization created successfully");
2403
+ outputOrganizationPretty(org);
2404
+ }
2405
+ } catch (error) {
2406
+ handleError(error, useJson);
2407
+ }
2408
+ });
2409
+ return organizations;
2410
+ }
2411
+
2412
+ // src/commands/products.ts
2413
+ import { Command as Command10 } from "commander";
2258
2414
  function createProductsCommand() {
2259
- const products = new Command9("products").description(
2415
+ const products = new Command10("products").description(
2260
2416
  "Manage products. A workspace contains products; a product holds features and tickets."
2261
2417
  );
2262
2418
  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) => {
@@ -2319,9 +2475,9 @@ function createProductsCommand() {
2319
2475
  }
2320
2476
 
2321
2477
  // src/commands/projects.ts
2322
- import { Command as Command10 } from "commander";
2478
+ import { Command as Command11 } from "commander";
2323
2479
  function createProjectsCommand() {
2324
- const projects = new Command10("projects").description("Manage projects");
2480
+ const projects = new Command11("projects").description("Manage projects");
2325
2481
  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) => {
2326
2482
  const globalOpts = cmd.optsWithGlobals();
2327
2483
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -2344,7 +2500,7 @@ function createProjectsCommand() {
2344
2500
  }
2345
2501
 
2346
2502
  // src/commands/tickets.ts
2347
- import { Command as Command11 } from "commander";
2503
+ import { Command as Command12 } from "commander";
2348
2504
  function collectRepeatable(value, previous) {
2349
2505
  return [...previous, value];
2350
2506
  }
@@ -2387,7 +2543,7 @@ function validateTicketType(value) {
2387
2543
  return value;
2388
2544
  }
2389
2545
  function createTicketsCommand() {
2390
- const tickets = new Command11("tickets").description(
2546
+ const tickets = new Command12("tickets").description(
2391
2547
  "Manage product backlog tickets. Tickets live under a product, link to features/epics, depend on each other, and can be implemented via actions."
2392
2548
  );
2393
2549
  tickets.command("list").description(
@@ -2700,7 +2856,7 @@ function createTicketsCommand() {
2700
2856
  handleError(error, useJson);
2701
2857
  }
2702
2858
  });
2703
- const comment = new Command11("comment").description("Manage comments on a ticket");
2859
+ const comment = new Command12("comment").description("Manage comments on a ticket");
2704
2860
  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) => {
2705
2861
  const globalOpts = cmd.optsWithGlobals();
2706
2862
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -2739,10 +2895,10 @@ function createTicketsCommand() {
2739
2895
  }
2740
2896
 
2741
2897
  // src/commands/workspaces.ts
2742
- import { Command as Command12 } from "commander";
2898
+ import { Command as Command13 } from "commander";
2743
2899
  import chalk5 from "chalk";
2744
2900
  function createWorkspacesCommand() {
2745
- const workspaces = new Command12("workspaces").description("Manage workspaces");
2901
+ const workspaces = new Command13("workspaces").description("Manage workspaces");
2746
2902
  workspaces.command("list").description("List all workspaces").action(async (_options, cmd) => {
2747
2903
  const globalOpts = cmd.optsWithGlobals();
2748
2904
  const useJson = shouldUseJson(globalOpts.json, globalOpts.pretty);
@@ -2788,7 +2944,7 @@ function createWorkspacesCommand() {
2788
2944
  }
2789
2945
 
2790
2946
  // src/index.ts
2791
- var program = new Command13();
2947
+ var program = new Command14();
2792
2948
  program.name("exponential").description(
2793
2949
  [
2794
2950
  "CLI to interact with Exponential productivity app.",
@@ -2805,6 +2961,7 @@ program.addCommand(createDealsCommand());
2805
2961
  program.addCommand(createEpicsCommand());
2806
2962
  program.addCommand(createFeaturesCommand());
2807
2963
  program.addCommand(createLabelsCommand());
2964
+ program.addCommand(createOrganizationsCommand());
2808
2965
  program.addCommand(createProductsCommand());
2809
2966
  program.addCommand(createProjectsCommand());
2810
2967
  program.addCommand(createTicketsCommand());