commet 1.10.0 → 1.11.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.
Files changed (2) hide show
  1. package/dist/index.js +700 -372
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -24,13 +24,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/index.ts
27
- var import_chalk16 = __toESM(require("chalk"));
27
+ var import_chalk15 = __toESM(require("chalk"));
28
28
  var import_commander13 = require("commander");
29
29
 
30
30
  // package.json
31
31
  var package_default = {
32
32
  name: "commet",
33
- version: "1.10.0",
33
+ version: "1.11.0",
34
34
  description: "Commet CLI - Manage your billing platform from the command line",
35
35
  bin: {
36
36
  commet: "./bin/commet"
@@ -87,16 +87,8 @@ var package_default = {
87
87
  }
88
88
  };
89
89
 
90
- // src/commands/create.ts
91
- var import_node_child_process = require("child_process");
92
- var fs2 = __toESM(require("fs"));
93
- var os2 = __toESM(require("os"));
94
- var path2 = __toESM(require("path"));
95
- var import_prompts = require("@inquirer/prompts");
96
- var import_chalk3 = __toESM(require("chalk"));
90
+ // src/commands/agent-info.ts
97
91
  var import_commander = require("commander");
98
- var import_ora2 = __toESM(require("ora"));
99
- var import_tar = require("tar");
100
92
 
101
93
  // src/utils/config.ts
102
94
  var fs = __toESM(require("fs"));
@@ -165,6 +157,211 @@ function clearProjectConfig() {
165
157
  }
166
158
  }
167
159
 
160
+ // src/utils/config-loader.ts
161
+ var fs2 = __toESM(require("fs"));
162
+ var path2 = __toESM(require("path"));
163
+ var import_jiti = require("jiti");
164
+ var CONFIG_NAMES = [
165
+ "commet.config.ts",
166
+ "commet.config.js",
167
+ "commet.config.mjs"
168
+ ];
169
+ function findConfigFile(cwd) {
170
+ for (const name of CONFIG_NAMES) {
171
+ const fullPath = path2.resolve(cwd, name);
172
+ if (fs2.existsSync(fullPath)) {
173
+ return fullPath;
174
+ }
175
+ }
176
+ return null;
177
+ }
178
+ async function loadBillingConfig(cwd) {
179
+ const configPath = findConfigFile(cwd);
180
+ if (!configPath) {
181
+ throw new Error(
182
+ `No commet.config.ts found in ${cwd}. Create one with defineConfig() or run 'commet pull' to generate it.`
183
+ );
184
+ }
185
+ const jiti = (0, import_jiti.createJiti)(configPath, { interopDefault: true });
186
+ const mod = await jiti.import(configPath);
187
+ if (!mod || typeof mod !== "object") {
188
+ throw new Error(`${configPath}: failed to load config module`);
189
+ }
190
+ const moduleRecord = mod;
191
+ if (!moduleRecord.default) {
192
+ throw new Error(
193
+ `${configPath}: must use \`export default defineConfig({...})\``
194
+ );
195
+ }
196
+ const config = moduleRecord.default;
197
+ validateConfig(config, configPath);
198
+ return { config, configPath };
199
+ }
200
+ var VALID_FEATURE_TYPES = /* @__PURE__ */ new Set(["boolean", "usage", "seats"]);
201
+ var VALID_INTERVALS = /* @__PURE__ */ new Set([
202
+ "weekly",
203
+ "monthly",
204
+ "quarterly",
205
+ "yearly",
206
+ "one_time"
207
+ ]);
208
+ function validateConfig(config, configPath) {
209
+ if (!config || typeof config !== "object") {
210
+ throw new Error(`${configPath}: config must be an object`);
211
+ }
212
+ if (!config.features || typeof config.features !== "object") {
213
+ throw new Error(`${configPath}: config.features must be an object`);
214
+ }
215
+ if (!config.plans || typeof config.plans !== "object") {
216
+ throw new Error(`${configPath}: config.plans must be an object`);
217
+ }
218
+ for (const [code, feature] of Object.entries(config.features)) {
219
+ if (!feature.name || typeof feature.name !== "string") {
220
+ throw new Error(`Feature "${code}": name is required`);
221
+ }
222
+ if (!VALID_FEATURE_TYPES.has(feature.type)) {
223
+ throw new Error(
224
+ `Feature "${code}": type must be one of: boolean, usage, seats`
225
+ );
226
+ }
227
+ }
228
+ for (const [code, plan] of Object.entries(config.plans)) {
229
+ if (!plan.name || typeof plan.name !== "string") {
230
+ throw new Error(`Plan "${code}": name is required`);
231
+ }
232
+ if (!Array.isArray(plan.prices)) {
233
+ throw new Error(`Plan "${code}": prices must be an array`);
234
+ }
235
+ for (const price of plan.prices) {
236
+ if (!VALID_INTERVALS.has(price.interval)) {
237
+ throw new Error(
238
+ `Plan "${code}": price interval "${price.interval}" is not valid`
239
+ );
240
+ }
241
+ if (typeof price.amount !== "number") {
242
+ throw new Error(`Plan "${code}": price amount must be a number`);
243
+ }
244
+ }
245
+ if (plan.prices.length > 0) {
246
+ if (!plan.defaultInterval) {
247
+ throw new Error(
248
+ `Plan "${code}": defaultInterval is required when prices are defined`
249
+ );
250
+ }
251
+ const priceIntervals = new Set(plan.prices.map((p) => p.interval));
252
+ if (!priceIntervals.has(plan.defaultInterval)) {
253
+ throw new Error(
254
+ `Plan "${code}": defaultInterval "${plan.defaultInterval}" does not match any price interval (${[...priceIntervals].join(", ")})`
255
+ );
256
+ }
257
+ }
258
+ if (plan.features) {
259
+ for (const featureCode of Object.keys(plan.features)) {
260
+ if (!config.features[featureCode]) {
261
+ throw new Error(
262
+ `Plan "${code}": references feature "${featureCode}" which is not defined in config.features`
263
+ );
264
+ }
265
+ }
266
+ }
267
+ }
268
+ }
269
+
270
+ // src/commands/agent-info.ts
271
+ var agentInfoCommand = new import_commander.Command("agent-info").description(
272
+ "Print project status and CLI capabilities as JSON. Designed for AI agents and CI pipelines \u2014 run this first to discover what commands are available and how to call them non-interactively."
273
+ ).action(() => {
274
+ const authenticated = authExists();
275
+ const projectConfig = projectConfigExists() ? loadProjectConfig() : null;
276
+ const configPath = findConfigFile(process.cwd());
277
+ const setup = [];
278
+ if (!authenticated) {
279
+ setup.push(
280
+ "Not authenticated. A human must run 'commet login' in a terminal with browser access \u2014 this is the only interactive step."
281
+ );
282
+ }
283
+ if (authenticated && !projectConfig) {
284
+ setup.push(
285
+ "No project linked. Run 'commet link --org <slug>' or 'commet orgs --json' to find available organizations."
286
+ );
287
+ }
288
+ const output = {
289
+ version: package_default.version,
290
+ authenticated,
291
+ ...setup.length > 0 ? { setup } : {},
292
+ project: projectConfig ? {
293
+ linked: true,
294
+ orgId: projectConfig.orgId,
295
+ orgName: projectConfig.orgName,
296
+ mode: projectConfig.mode
297
+ } : { linked: false },
298
+ config: {
299
+ exists: configPath !== null,
300
+ path: configPath?.split("/").pop() ?? null
301
+ },
302
+ commands: {
303
+ pull: {
304
+ description: "Pull remote config and generate commet.config.ts",
305
+ usage: "commet pull --json --yes",
306
+ preview: "commet pull --json --dry-run"
307
+ },
308
+ push: {
309
+ description: "Push commet.config.ts to remote",
310
+ usage: "commet push --json --yes",
311
+ preview: "commet push --json --dry-run"
312
+ },
313
+ list: {
314
+ description: "List resources from remote",
315
+ usage: "commet list <features|plans|seats> --json"
316
+ },
317
+ orgs: {
318
+ description: "List available organizations",
319
+ usage: "commet orgs --json"
320
+ },
321
+ link: {
322
+ description: "Link project to an organization",
323
+ usage: "commet link --org <slug-or-id>"
324
+ },
325
+ switch: {
326
+ description: "Switch to a different organization",
327
+ usage: "commet switch --org <slug-or-id>"
328
+ },
329
+ listen: {
330
+ description: "Forward webhook events to local server. Long-running streaming process \u2014 run in background.",
331
+ usage: "commet listen <url> [--events <types>]"
332
+ },
333
+ create: {
334
+ description: "Scaffold a new Commet app from template",
335
+ usage: "commet create [name] -t <template> --org <slug> -y"
336
+ },
337
+ unlink: {
338
+ description: "Unlink project from organization",
339
+ usage: "commet unlink"
340
+ },
341
+ login: {
342
+ description: "Authenticate via browser. Requires a human \u2014 opens a device-code flow that must be confirmed in a browser.",
343
+ usage: "commet login"
344
+ },
345
+ logout: {
346
+ description: "Log out of Commet",
347
+ usage: "commet logout"
348
+ }
349
+ }
350
+ };
351
+ console.log(JSON.stringify(output, null, 2));
352
+ });
353
+
354
+ // src/commands/create.ts
355
+ var import_node_child_process = require("child_process");
356
+ var fs3 = __toESM(require("fs"));
357
+ var os2 = __toESM(require("os"));
358
+ var path3 = __toESM(require("path"));
359
+ var import_prompts = require("@inquirer/prompts");
360
+ var import_chalk3 = __toESM(require("chalk"));
361
+ var import_commander2 = require("commander");
362
+ var import_ora2 = __toESM(require("ora"));
363
+ var import_tar = require("tar");
364
+
168
365
  // src/utils/api.ts
169
366
  var BASE_URL = "https://commet.co";
170
367
  async function apiRequest(endpoint, options = {}) {
@@ -363,11 +560,11 @@ async function downloadTemplate(templateDir, dest, ref) {
363
560
  if (!response.ok) {
364
561
  throw new Error(`Failed to download (HTTP ${response.status})`);
365
562
  }
366
- const tempFile = path2.join(os2.tmpdir(), `commet-${Date.now()}.tar.gz`);
563
+ const tempFile = path3.join(os2.tmpdir(), `commet-${Date.now()}.tar.gz`);
367
564
  try {
368
565
  const buffer = Buffer.from(await response.arrayBuffer());
369
- fs2.writeFileSync(tempFile, buffer);
370
- fs2.mkdirSync(dest, { recursive: true });
566
+ fs3.writeFileSync(tempFile, buffer);
567
+ fs3.mkdirSync(dest, { recursive: true });
371
568
  await (0, import_tar.extract)({
372
569
  file: tempFile,
373
570
  cwd: dest,
@@ -378,22 +575,22 @@ async function downloadTemplate(templateDir, dest, ref) {
378
575
  }
379
576
  });
380
577
  } finally {
381
- if (fs2.existsSync(tempFile)) {
382
- fs2.unlinkSync(tempFile);
578
+ if (fs3.existsSync(tempFile)) {
579
+ fs3.unlinkSync(tempFile);
383
580
  }
384
581
  }
385
- const files = fs2.readdirSync(dest);
582
+ const files = fs3.readdirSync(dest);
386
583
  if (files.length === 0) {
387
584
  throw new Error(`Template "${templateDir}" not found in repository`);
388
585
  }
389
586
  }
390
587
  function updatePackageJson(dest, projectName) {
391
- const pkgPath = path2.join(dest, "package.json");
392
- const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
588
+ const pkgPath = path3.join(dest, "package.json");
589
+ const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
393
590
  pkg.name = projectName;
394
591
  pkg.version = "0.0.1";
395
592
  pkg.private = true;
396
- fs2.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
593
+ fs3.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
397
594
  `);
398
595
  }
399
596
  async function fetchLatestVersion(packageName) {
@@ -409,8 +606,8 @@ async function fetchLatestVersion(packageName) {
409
606
  return data.version;
410
607
  }
411
608
  async function resolveWorkspaceDeps(dest) {
412
- const pkgPath = path2.join(dest, "package.json");
413
- const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
609
+ const pkgPath = path3.join(dest, "package.json");
610
+ const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
414
611
  const depSections = [
415
612
  "dependencies",
416
613
  "devDependencies",
@@ -447,29 +644,29 @@ async function resolveWorkspaceDeps(dest) {
447
644
  pkg[section][name] = version;
448
645
  }
449
646
  }
450
- fs2.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
647
+ fs3.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
451
648
  `);
452
649
  return workspaceDeps.size;
453
650
  }
454
651
  function copyEnvExample(dest) {
455
- const examplePath = path2.join(dest, ".env.example");
456
- const envPath = path2.join(dest, ".env");
457
- if (fs2.existsSync(examplePath)) {
458
- fs2.copyFileSync(examplePath, envPath);
652
+ const examplePath = path3.join(dest, ".env.example");
653
+ const envPath = path3.join(dest, ".env");
654
+ if (fs3.existsSync(examplePath)) {
655
+ fs3.copyFileSync(examplePath, envPath);
459
656
  }
460
657
  }
461
658
  function writeApiKeyToEnv(dest, apiKey) {
462
- const envPath = path2.join(dest, ".env");
463
- if (!fs2.existsSync(envPath)) return;
464
- let content = fs2.readFileSync(envPath, "utf-8");
659
+ const envPath = path3.join(dest, ".env");
660
+ if (!fs3.existsSync(envPath)) return;
661
+ let content = fs3.readFileSync(envPath, "utf-8");
465
662
  content = content.replace(/COMMET_API_KEY=.*/, `COMMET_API_KEY=${apiKey}`);
466
- fs2.writeFileSync(envPath, content);
663
+ fs3.writeFileSync(envPath, content);
467
664
  }
468
665
  function linkProject(dest, orgId, orgName) {
469
- const commetDir = path2.join(dest, ".commet");
470
- fs2.mkdirSync(commetDir, { recursive: true });
471
- fs2.writeFileSync(
472
- path2.join(commetDir, "config.json"),
666
+ const commetDir = path3.join(dest, ".commet");
667
+ fs3.mkdirSync(commetDir, { recursive: true });
668
+ fs3.writeFileSync(
669
+ path3.join(commetDir, "config.json"),
473
670
  JSON.stringify({ orgId, orgName, mode: "sandbox" }, null, 2),
474
671
  "utf8"
475
672
  );
@@ -508,7 +705,7 @@ async function installSkills(projectRoot) {
508
705
  });
509
706
  });
510
707
  }
511
- var createCommand = new import_commander.Command("create").description("Create a new Commet app from a template").argument("[name]", "Project name").option(
708
+ var createCommand = new import_commander2.Command("create").description("Create a new Commet app from a template").argument("[name]", "Project name").option(
512
709
  "-t, --template <template>",
513
710
  "Template to use (fixed, seats, metered, credits, balance-ai, balance-fixed)"
514
711
  ).option("--org <slug>", "Organization slug or ID (skips selection)").option("--skills", "Install agent skills").option("--no-skills", "Skip agent skills installation").option("-y, --yes", "Accept defaults for optional prompts").option("--ref <ref>", "Git ref to fetch templates from", "main").option("--list", "List available templates").action(async (argName, opts) => {
@@ -541,8 +738,8 @@ var createCommand = new import_commander.Command("create").description("Create a
541
738
  return;
542
739
  }
543
740
  }
544
- const dest = path2.resolve(projectName);
545
- if (fs2.existsSync(dest)) {
741
+ const dest = path3.resolve(projectName);
742
+ if (fs3.existsSync(dest)) {
546
743
  console.log(
547
744
  import_chalk3.default.red(`\u2717 Directory "${projectName}" already exists`)
548
745
  );
@@ -662,8 +859,8 @@ var createCommand = new import_commander.Command("create").description("Create a
662
859
  if (error instanceof Error) {
663
860
  console.error(import_chalk3.default.red(error.message));
664
861
  }
665
- if (fs2.existsSync(dest)) {
666
- fs2.rmSync(dest, { recursive: true, force: true });
862
+ if (fs3.existsSync(dest)) {
863
+ fs3.rmSync(dest, { recursive: true, force: true });
667
864
  }
668
865
  return;
669
866
  }
@@ -682,8 +879,8 @@ var createCommand = new import_commander.Command("create").description("Create a
682
879
  if (error instanceof Error) {
683
880
  console.error(import_chalk3.default.red(error.message));
684
881
  }
685
- if (fs2.existsSync(dest)) {
686
- fs2.rmSync(dest, { recursive: true, force: true });
882
+ if (fs3.existsSync(dest)) {
883
+ fs3.rmSync(dest, { recursive: true, force: true });
687
884
  }
688
885
  return;
689
886
  }
@@ -734,54 +931,22 @@ var createCommand = new import_commander.Command("create").description("Create a
734
931
  console.log();
735
932
  });
736
933
 
737
- // src/commands/info.ts
738
- var import_chalk4 = __toESM(require("chalk"));
739
- var import_commander2 = require("commander");
740
- var infoCommand = new import_commander2.Command("info").description("Display information about the current project").action(async () => {
741
- console.log(import_chalk4.default.bold("\n\u{1F4E6} Project Information\n"));
742
- if (!authExists()) {
743
- console.log(import_chalk4.default.yellow("Authentication: Not logged in"));
744
- console.log(import_chalk4.default.dim("Run `commet login` to authenticate\n"));
745
- return;
746
- }
747
- console.log(import_chalk4.default.green("Authentication: Logged in \u2713"));
748
- if (!projectConfigExists()) {
749
- console.log(import_chalk4.default.yellow("\nProject: Not linked"));
750
- console.log(
751
- import_chalk4.default.dim("Run `commet link` to connect to an organization\n")
752
- );
753
- return;
754
- }
755
- const projectConfig = loadProjectConfig();
756
- if (!projectConfig) {
757
- console.log(import_chalk4.default.red("\nProject: Invalid configuration"));
758
- return;
759
- }
760
- console.log(import_chalk4.default.green("\nProject: Linked \u2713"));
761
- console.log(import_chalk4.default.dim(" Organization:"), projectConfig.orgName);
762
- console.log(import_chalk4.default.dim(" Organization ID:"), projectConfig.orgId);
763
- console.log(import_chalk4.default.dim(" Mode:"), projectConfig.mode);
764
- console.log(
765
- import_chalk4.default.dim("\nRun `commet pull` to generate type definitions\n")
766
- );
767
- });
768
-
769
934
  // src/commands/link.ts
770
935
  var import_prompts2 = require("@inquirer/prompts");
771
- var import_chalk5 = __toESM(require("chalk"));
936
+ var import_chalk4 = __toESM(require("chalk"));
772
937
  var import_commander3 = require("commander");
773
938
  var import_ora3 = __toESM(require("ora"));
774
939
 
775
940
  // src/utils/gitignore-updater.ts
776
- var fs3 = __toESM(require("fs"));
777
- var path3 = __toESM(require("path"));
941
+ var fs4 = __toESM(require("fs"));
942
+ var path4 = __toESM(require("path"));
778
943
  function updateGitignore(entry) {
779
944
  const cwd = process.cwd();
780
- const gitignorePath = path3.join(cwd, ".gitignore");
945
+ const gitignorePath = path4.join(cwd, ".gitignore");
781
946
  try {
782
947
  let content = "";
783
- if (fs3.existsSync(gitignorePath)) {
784
- content = fs3.readFileSync(gitignorePath, "utf8");
948
+ if (fs4.existsSync(gitignorePath)) {
949
+ content = fs4.readFileSync(gitignorePath, "utf8");
785
950
  const lines = content.split("\n");
786
951
  for (const line of lines) {
787
952
  const trimmed = line.trim();
@@ -795,7 +960,7 @@ function updateGitignore(entry) {
795
960
  }
796
961
  content += `${entry}
797
962
  `;
798
- fs3.writeFileSync(gitignorePath, content, "utf8");
963
+ fs4.writeFileSync(gitignorePath, content, "utf8");
799
964
  return { success: true };
800
965
  } catch (error) {
801
966
  return {
@@ -806,20 +971,34 @@ function updateGitignore(entry) {
806
971
  }
807
972
 
808
973
  // src/commands/link.ts
809
- var linkCommand = new import_commander3.Command("link").description("Link this project to a Commet organization").action(async () => {
974
+ var linkCommand = new import_commander3.Command("link").description(
975
+ "Link this project directory to a Commet organization. Creates .commet/config.json with the selected org."
976
+ ).option(
977
+ "--org <slug-or-id>",
978
+ "Organization slug or ID \u2014 skips interactive selection"
979
+ ).addHelpText(
980
+ "after",
981
+ `
982
+ Examples:
983
+ $ commet link Interactive \u2014 choose from a list
984
+ $ commet link --org acme Non-interactive \u2014 match by slug or ID
985
+
986
+ Use 'commet orgs' to see available organizations and their slugs.
987
+ `
988
+ ).action(async (options) => {
810
989
  if (!authExists()) {
811
- console.log(import_chalk5.default.red("\u2717 Not authenticated"));
812
- console.log(import_chalk5.default.dim("Run `commet login` first"));
813
- return;
990
+ console.log(import_chalk4.default.red("\u2717 Not authenticated"));
991
+ console.log(import_chalk4.default.dim("Run `commet login` first"));
992
+ process.exit(1);
814
993
  }
815
994
  if (projectConfigExists()) {
816
995
  const config = loadProjectConfig();
817
- console.log(import_chalk5.default.yellow("\u26A0 This project is already linked"));
996
+ console.log(import_chalk4.default.yellow("\u26A0 This project is already linked"));
818
997
  console.log(
819
- import_chalk5.default.dim(`Organization: ${config?.orgName} \xB7 ${config?.mode}`)
998
+ import_chalk4.default.dim(`Organization: ${config?.orgName} \xB7 ${config?.mode}`)
820
999
  );
821
1000
  console.log(
822
- import_chalk5.default.dim(
1001
+ import_chalk4.default.dim(
823
1002
  "\nRun `commet unlink` first if you want to change the organization"
824
1003
  )
825
1004
  );
@@ -831,37 +1010,52 @@ var linkCommand = new import_commander3.Command("link").description("Link this p
831
1010
  );
832
1011
  if (result.error || !result.data) {
833
1012
  spinner.fail("Failed to fetch organizations");
834
- console.error(import_chalk5.default.red("Error:"), result.error);
835
- return;
1013
+ console.error(import_chalk4.default.red("Error:"), result.error);
1014
+ process.exit(1);
836
1015
  }
837
1016
  const { organizations } = result.data;
838
1017
  if (organizations.length === 0) {
839
1018
  spinner.stop();
840
- console.log(import_chalk5.default.yellow("\u26A0 No organizations found"));
1019
+ console.log(import_chalk4.default.yellow("\u26A0 No organizations found"));
841
1020
  console.log(
842
- import_chalk5.default.dim("Create an organization at https://commet.co first")
1021
+ import_chalk4.default.dim("Create an organization at https://commet.co first")
843
1022
  );
844
1023
  return;
845
1024
  }
846
1025
  spinner.stop();
847
- let orgId;
848
- try {
849
- orgId = await (0, import_prompts2.select)({
850
- message: "Select organization:",
851
- choices: organizations.map((org) => ({
852
- name: `${org.name} ${import_chalk5.default.dim(`(${org.slug}) \xB7 ${org.mode}`)}`,
853
- value: org.id
854
- })),
855
- theme: promptTheme
856
- });
857
- } catch (_error) {
858
- console.log(import_chalk5.default.yellow("\n\u26A0 Link cancelled"));
859
- return;
860
- }
861
- const selectedOrg = organizations.find((org) => org.id === orgId);
862
- if (!selectedOrg) {
863
- console.log(import_chalk5.default.red("\u2717 Organization not found"));
864
- return;
1026
+ let selectedOrg;
1027
+ if (options.org) {
1028
+ selectedOrg = organizations.find(
1029
+ (org) => org.slug === options.org || org.id === options.org
1030
+ );
1031
+ if (!selectedOrg) {
1032
+ console.log(import_chalk4.default.red(`\u2717 Organization "${options.org}" not found`));
1033
+ console.log(import_chalk4.default.dim("\nAvailable organizations:"));
1034
+ for (const org of organizations) {
1035
+ console.log(import_chalk4.default.dim(` ${org.slug} (${org.mode})`));
1036
+ }
1037
+ process.exit(1);
1038
+ }
1039
+ } else {
1040
+ let orgId;
1041
+ try {
1042
+ orgId = await (0, import_prompts2.select)({
1043
+ message: "Select organization:",
1044
+ choices: organizations.map((org) => ({
1045
+ name: `${org.name} ${import_chalk4.default.dim(`(${org.slug}) \xB7 ${org.mode}`)}`,
1046
+ value: org.id
1047
+ })),
1048
+ theme: promptTheme
1049
+ });
1050
+ } catch (_error) {
1051
+ console.log(import_chalk4.default.yellow("\n\u26A0 Link cancelled"));
1052
+ return;
1053
+ }
1054
+ selectedOrg = organizations.find((org) => org.id === orgId);
1055
+ if (!selectedOrg) {
1056
+ console.log(import_chalk4.default.red("\u2717 Organization not found"));
1057
+ process.exit(1);
1058
+ }
865
1059
  }
866
1060
  saveProjectConfig({
867
1061
  orgId: selectedOrg.id,
@@ -869,119 +1063,171 @@ var linkCommand = new import_commander3.Command("link").description("Link this p
869
1063
  mode: selectedOrg.mode
870
1064
  });
871
1065
  const gitignoreResult = updateGitignore(".commet/");
872
- console.log(import_chalk5.default.green("\n\u2713 Project linked successfully"));
1066
+ console.log(import_chalk4.default.green("\n\u2713 Project linked successfully"));
873
1067
  console.log(
874
- import_chalk5.default.dim(`Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
1068
+ import_chalk4.default.dim(`Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
875
1069
  );
876
1070
  if (gitignoreResult.success) {
877
- console.log(import_chalk5.default.green("\u2713 Updated .gitignore"));
1071
+ console.log(import_chalk4.default.green("\u2713 Updated .gitignore"));
878
1072
  } else {
879
- console.log(import_chalk5.default.yellow("\u26A0 No .gitignore found"));
880
- console.log(import_chalk5.default.dim("Add .commet/ to your .gitignore file"));
1073
+ console.log(import_chalk4.default.yellow("\u26A0 No .gitignore found"));
1074
+ console.log(import_chalk4.default.dim("Add .commet/ to your .gitignore file"));
881
1075
  }
882
- console.log(import_chalk5.default.dim("\nRun 'commet pull' to generate TypeScript types"));
1076
+ console.log(import_chalk4.default.dim("\nRun 'commet pull' to generate TypeScript types"));
883
1077
  });
884
1078
 
885
1079
  // src/commands/list.ts
886
- var import_chalk6 = __toESM(require("chalk"));
1080
+ var import_chalk5 = __toESM(require("chalk"));
887
1081
  var import_commander4 = require("commander");
888
1082
  var import_ora4 = __toESM(require("ora"));
889
1083
  var validTypes = ["features", "seats", "plans"];
890
- var listCommand = new import_commander4.Command("list").description("List features, seat types, or plans").argument("<type>", "Type to list (features, seats, or plans)").action(async (type) => {
1084
+ var listCommand = new import_commander4.Command("list").description(
1085
+ "List resources from your linked organization. Shows features, seat types, or plans currently configured on remote."
1086
+ ).argument("<type>", "What to list: features, seats, or plans").option("--json", "Output as JSON array").addHelpText(
1087
+ "after",
1088
+ `
1089
+ Examples:
1090
+ $ commet list features Show all features with type and description
1091
+ $ commet list plans Show all plans
1092
+ $ commet list seats Show all seat types
1093
+ $ commet list features --json JSON array for agent/CI use
1094
+ `
1095
+ ).action(async (type, options) => {
1096
+ const jsonMode = options.json;
891
1097
  if (!validTypes.includes(type)) {
892
- console.log(
893
- import_chalk6.default.red('\u2717 Invalid type. Use "features", "seats", or "plans"')
894
- );
895
- console.log(import_chalk6.default.dim("\nExamples:"));
896
- console.log(import_chalk6.default.dim(" commet list features"));
897
- console.log(import_chalk6.default.dim(" commet list seats"));
898
- console.log(import_chalk6.default.dim(" commet list plans"));
899
- return;
1098
+ if (jsonMode) {
1099
+ console.log(
1100
+ JSON.stringify({
1101
+ error: `Invalid type "${type}". Use: features, seats, or plans`
1102
+ })
1103
+ );
1104
+ } else {
1105
+ console.log(
1106
+ import_chalk5.default.red('\u2717 Invalid type. Use "features", "seats", or "plans"')
1107
+ );
1108
+ console.log(import_chalk5.default.dim("\nExamples:"));
1109
+ console.log(import_chalk5.default.dim(" commet list features"));
1110
+ console.log(import_chalk5.default.dim(" commet list seats"));
1111
+ console.log(import_chalk5.default.dim(" commet list plans"));
1112
+ }
1113
+ process.exit(1);
900
1114
  }
901
1115
  if (!authExists()) {
902
- console.log(import_chalk6.default.red("\u2717 Not authenticated"));
903
- console.log(import_chalk6.default.dim("Run `commet login` first"));
904
- return;
1116
+ if (jsonMode) {
1117
+ console.log(JSON.stringify({ error: "Not authenticated" }));
1118
+ } else {
1119
+ console.log(import_chalk5.default.red("\u2717 Not authenticated"));
1120
+ console.log(import_chalk5.default.dim("Run `commet login` first"));
1121
+ }
1122
+ process.exit(1);
905
1123
  }
906
1124
  if (!projectConfigExists()) {
907
- console.log(import_chalk6.default.red("\u2717 Project not linked"));
908
- console.log(
909
- import_chalk6.default.dim("Run `commet link` first to connect to an organization")
910
- );
911
- return;
1125
+ if (jsonMode) {
1126
+ console.log(JSON.stringify({ error: "Project not linked" }));
1127
+ } else {
1128
+ console.log(import_chalk5.default.red("\u2717 Project not linked"));
1129
+ console.log(
1130
+ import_chalk5.default.dim("Run `commet link` first to connect to an organization")
1131
+ );
1132
+ }
1133
+ process.exit(1);
912
1134
  }
913
1135
  const projectConfig = loadProjectConfig();
914
1136
  if (!projectConfig) {
915
- console.log(import_chalk6.default.red("\u2717 Invalid project configuration"));
916
- return;
1137
+ if (jsonMode) {
1138
+ console.log(JSON.stringify({ error: "Invalid project configuration" }));
1139
+ } else {
1140
+ console.log(import_chalk5.default.red("\u2717 Invalid project configuration"));
1141
+ }
1142
+ process.exit(1);
917
1143
  }
918
- const spinner = (0, import_ora4.default)(`Fetching ${type}...`).start();
1144
+ const spinner = jsonMode ? null : (0, import_ora4.default)(`Fetching ${type}...`).start();
919
1145
  const result = await apiRequest(
920
1146
  `${BASE_URL}/api/cli/pull?orgId=${projectConfig.orgId}`
921
1147
  );
922
1148
  if (result.error || !result.data) {
923
- spinner.fail(`Failed to fetch ${type}`);
924
- console.error(import_chalk6.default.red("Error:"), result.error);
925
- return;
1149
+ if (jsonMode) {
1150
+ console.log(JSON.stringify({ error: result.error }));
1151
+ } else {
1152
+ spinner?.fail(`Failed to fetch ${type}`);
1153
+ console.error(import_chalk5.default.red("Error:"), result.error);
1154
+ }
1155
+ process.exit(1);
926
1156
  }
927
- spinner.stop();
1157
+ spinner?.stop();
928
1158
  if (type === "features") {
929
1159
  const { features } = result.data;
1160
+ if (jsonMode) {
1161
+ console.log(JSON.stringify(features));
1162
+ return;
1163
+ }
930
1164
  if (features.length === 0) {
931
- console.log(import_chalk6.default.yellow("\u26A0 No features found"));
1165
+ console.log(import_chalk5.default.yellow("\u26A0 No features found"));
932
1166
  console.log(
933
- import_chalk6.default.dim("Create features in your Commet dashboard first")
1167
+ import_chalk5.default.dim("Create features in your Commet dashboard first")
934
1168
  );
935
1169
  return;
936
1170
  }
937
- console.log(import_chalk6.default.bold(`
938
- \u{1F4CA} Features (${features.length})
1171
+ console.log(import_chalk5.default.bold(`
1172
+ Features (${features.length})
939
1173
  `));
940
1174
  for (const feature of features) {
941
- console.log(import_chalk6.default.green(`\u2022 ${feature.code} (${feature.type})`));
942
- console.log(import_chalk6.default.dim(` ${feature.name}`));
1175
+ console.log(
1176
+ import_chalk5.default.green(` ${feature.code} ${import_chalk5.default.dim(`(${feature.type})`)}`)
1177
+ );
1178
+ console.log(import_chalk5.default.dim(` ${feature.name}`));
943
1179
  if (feature.description) {
944
- console.log(import_chalk6.default.dim(` ${feature.description}`));
1180
+ console.log(import_chalk5.default.dim(` ${feature.description}`));
945
1181
  }
946
1182
  console.log("");
947
1183
  }
948
1184
  } else if (type === "seats") {
949
1185
  const { seatTypes } = result.data;
1186
+ if (jsonMode) {
1187
+ console.log(JSON.stringify(seatTypes));
1188
+ return;
1189
+ }
950
1190
  if (seatTypes.length === 0) {
951
- console.log(import_chalk6.default.yellow("\u26A0 No seat types found"));
1191
+ console.log(import_chalk5.default.yellow("\u26A0 No seat types found"));
952
1192
  console.log(
953
- import_chalk6.default.dim("Create seat types in your Commet dashboard first")
1193
+ import_chalk5.default.dim("Create seat types in your Commet dashboard first")
954
1194
  );
955
1195
  return;
956
1196
  }
957
- console.log(import_chalk6.default.bold(`
958
- \u{1F4BA} Seat Types (${seatTypes.length})
1197
+ console.log(import_chalk5.default.bold(`
1198
+ Seat Types (${seatTypes.length})
959
1199
  `));
960
1200
  for (const seatType of seatTypes) {
961
1201
  console.log(
962
- import_chalk6.default.green(`\u2022 ${seatType.code}${seatType.isFree ? " (Free)" : ""}`)
1202
+ import_chalk5.default.green(
1203
+ ` ${seatType.code}${seatType.isFree ? import_chalk5.default.dim(" (free)") : ""}`
1204
+ )
963
1205
  );
964
- console.log(import_chalk6.default.dim(` ${seatType.name}`));
1206
+ console.log(import_chalk5.default.dim(` ${seatType.name}`));
965
1207
  if (seatType.description) {
966
- console.log(import_chalk6.default.dim(` ${seatType.description}`));
1208
+ console.log(import_chalk5.default.dim(` ${seatType.description}`));
967
1209
  }
968
1210
  console.log("");
969
1211
  }
970
1212
  } else {
971
1213
  const { plans } = result.data;
1214
+ if (jsonMode) {
1215
+ console.log(JSON.stringify(plans));
1216
+ return;
1217
+ }
972
1218
  if (plans.length === 0) {
973
- console.log(import_chalk6.default.yellow("\u26A0 No plans found"));
974
- console.log(import_chalk6.default.dim("Create plans in your Commet dashboard first"));
1219
+ console.log(import_chalk5.default.yellow("\u26A0 No plans found"));
1220
+ console.log(import_chalk5.default.dim("Create plans in your Commet dashboard first"));
975
1221
  return;
976
1222
  }
977
- console.log(import_chalk6.default.bold(`
978
- \u{1F4CB} Plans (${plans.length})
1223
+ console.log(import_chalk5.default.bold(`
1224
+ Plans (${plans.length})
979
1225
  `));
980
1226
  for (const plan of plans) {
981
- console.log(import_chalk6.default.green(`\u2022 ${plan.code}`));
982
- console.log(import_chalk6.default.dim(` ${plan.name}`));
1227
+ console.log(import_chalk5.default.green(` ${plan.code}`));
1228
+ console.log(import_chalk5.default.dim(` ${plan.name}`));
983
1229
  if (plan.description) {
984
- console.log(import_chalk6.default.dim(` ${plan.description}`));
1230
+ console.log(import_chalk5.default.dim(` ${plan.description}`));
985
1231
  }
986
1232
  console.log("");
987
1233
  }
@@ -990,21 +1236,21 @@ var listCommand = new import_commander4.Command("list").description("List featur
990
1236
 
991
1237
  // src/commands/listen.ts
992
1238
  var import_ably = __toESM(require("ably"));
993
- var import_chalk7 = __toESM(require("chalk"));
1239
+ var import_chalk6 = __toESM(require("chalk"));
994
1240
  var import_commander5 = require("commander");
995
1241
  function printEventLine(line) {
996
1242
  const time = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
997
- const eventName = import_chalk7.default.yellow(line.event.padEnd(28));
998
- const timing = import_chalk7.default.dim(`(${line.ms}ms)`);
1243
+ const eventName = import_chalk6.default.yellow(line.event.padEnd(28));
1244
+ const timing = import_chalk6.default.dim(`(${line.ms}ms)`);
999
1245
  if ("error" in line) {
1000
1246
  console.log(
1001
- ` ${import_chalk7.default.dim(time)} ${eventName} \u2192 ${import_chalk7.default.red("Error")} ${timing}`
1247
+ ` ${import_chalk6.default.dim(time)} ${eventName} \u2192 ${import_chalk6.default.red("Error")} ${timing}`
1002
1248
  );
1003
- console.log(` ${" ".repeat(12)}${import_chalk7.default.red(line.error)}`);
1249
+ console.log(` ${" ".repeat(12)}${import_chalk6.default.red(line.error)}`);
1004
1250
  return;
1005
1251
  }
1006
- const status = line.statusCode < 400 ? import_chalk7.default.green(`${line.statusCode} OK`) : import_chalk7.default.red(`${line.statusCode} Error`);
1007
- console.log(` ${import_chalk7.default.dim(time)} ${eventName} \u2192 ${status} ${timing}`);
1252
+ const status = line.statusCode < 400 ? import_chalk6.default.green(`${line.statusCode} OK`) : import_chalk6.default.red(`${line.statusCode} Error`);
1253
+ console.log(` ${import_chalk6.default.dim(time)} ${eventName} \u2192 ${status} ${timing}`);
1008
1254
  }
1009
1255
  function isListenMessage(data) {
1010
1256
  if (typeof data !== "object" || data === null) return false;
@@ -1027,18 +1273,31 @@ function resolveTargetUrl(input2) {
1027
1273
  }
1028
1274
  return parsed.toString();
1029
1275
  }
1030
- var listenCommand = new import_commander5.Command("listen").description("Forward webhook events to your local server").argument(
1276
+ var listenCommand = new import_commander5.Command("listen").description(
1277
+ "Forward webhook events from Commet to your local server in real time. Opens a persistent connection and replays every event as an HTTP POST to your URL."
1278
+ ).argument(
1031
1279
  "<url>",
1032
- "URL to forward to (e.g. localhost:3000, local.commet.co:3010/webhooks, or just a port)"
1033
- ).option("--events <types>", "Comma-separated event types to filter").action(async (url, options) => {
1280
+ "Target URL \u2014 a port (3000), host:port (localhost:3000), or full URL"
1281
+ ).option(
1282
+ "--events <types>",
1283
+ "Only forward these event types (comma-separated)"
1284
+ ).addHelpText(
1285
+ "after",
1286
+ `
1287
+ Examples:
1288
+ $ commet listen 3000 Forward to http://localhost:3000/
1289
+ $ commet listen localhost:3000/webhooks Forward to a specific path
1290
+ $ commet listen 3000 --events invoice.paid Only forward invoice.paid events
1291
+ `
1292
+ ).action(async (url, options) => {
1034
1293
  const auth = loadAuth();
1035
1294
  if (!auth) {
1036
- console.log(import_chalk7.default.red("Not authenticated. Run: commet login"));
1295
+ console.log(import_chalk6.default.red("Not authenticated. Run: commet login"));
1037
1296
  process.exit(1);
1038
1297
  }
1039
1298
  const projectConfig = loadProjectConfig();
1040
1299
  if (!projectConfig) {
1041
- console.log(import_chalk7.default.red("No project linked. Run: commet link"));
1300
+ console.log(import_chalk6.default.red("No project linked. Run: commet link"));
1042
1301
  process.exit(1);
1043
1302
  }
1044
1303
  let targetUrl;
@@ -1046,7 +1305,7 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
1046
1305
  targetUrl = resolveTargetUrl(url);
1047
1306
  } catch (error) {
1048
1307
  console.log(
1049
- import_chalk7.default.red(error instanceof Error ? error.message : "Invalid URL")
1308
+ import_chalk6.default.red(error instanceof Error ? error.message : "Invalid URL")
1050
1309
  );
1051
1310
  process.exit(1);
1052
1311
  }
@@ -1059,9 +1318,9 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
1059
1318
  }
1060
1319
  );
1061
1320
  if (result.error || !result.data) {
1062
- console.log(import_chalk7.default.red("Failed to start listen session"));
1321
+ console.log(import_chalk6.default.red("Failed to start listen session"));
1063
1322
  if (result.error) {
1064
- console.log(import_chalk7.default.dim(result.error));
1323
+ console.log(import_chalk6.default.dim(result.error));
1065
1324
  }
1066
1325
  process.exit(1);
1067
1326
  }
@@ -1107,27 +1366,27 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
1107
1366
  let wasConnected = false;
1108
1367
  ably.connection.on("connected", () => {
1109
1368
  if (wasConnected) {
1110
- console.log(import_chalk7.default.green(" \u2713 Reconnected"));
1369
+ console.log(import_chalk6.default.green(" \u2713 Reconnected"));
1111
1370
  }
1112
1371
  wasConnected = true;
1113
1372
  });
1114
1373
  ably.connection.on("disconnected", () => {
1115
- console.log(import_chalk7.default.yellow("\n \u26A0 Disconnected. Reconnecting..."));
1374
+ console.log(import_chalk6.default.yellow("\n \u26A0 Disconnected. Reconnecting..."));
1116
1375
  });
1117
1376
  ably.connection.on("failed", () => {
1118
1377
  console.log(
1119
- import_chalk7.default.red("\n \u2717 Connection failed. Check your authentication.")
1378
+ import_chalk6.default.red("\n \u2717 Connection failed. Check your authentication.")
1120
1379
  );
1121
1380
  process.exit(1);
1122
1381
  });
1123
1382
  const channel = ably.channels.get(channelName);
1124
1383
  console.log("");
1125
1384
  console.log(
1126
- import_chalk7.default.green(` \u2713 Authenticated (org: ${projectConfig.orgName})`)
1385
+ import_chalk6.default.green(` \u2713 Authenticated (org: ${projectConfig.orgName})`)
1127
1386
  );
1128
- console.log(import_chalk7.default.green(" \u2713 Connected to Commet webhook stream"));
1129
- console.log(import_chalk7.default.cyan(` \u27F6 Forwarding to ${targetUrl}`));
1130
- console.log(import_chalk7.default.dim(` \u27F6 Signing secret: ${signingSecret}`));
1387
+ console.log(import_chalk6.default.green(" \u2713 Connected to Commet webhook stream"));
1388
+ console.log(import_chalk6.default.cyan(` \u27F6 Forwarding to ${targetUrl}`));
1389
+ console.log(import_chalk6.default.dim(` \u27F6 Signing secret: ${signingSecret}`));
1131
1390
  console.log("");
1132
1391
  console.log(" Ready! Listening for webhook events...");
1133
1392
  console.log("");
@@ -1158,7 +1417,7 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
1158
1417
  process.on("SIGINT", async () => {
1159
1418
  if (isShuttingDown) return;
1160
1419
  isShuttingDown = true;
1161
- console.log(import_chalk7.default.dim("\n Disconnecting..."));
1420
+ console.log(import_chalk6.default.dim("\n Disconnecting..."));
1162
1421
  ably.close();
1163
1422
  await apiRequest(`${BASE_URL}/api/cli/listen/stop`, {
1164
1423
  method: "POST",
@@ -1172,13 +1431,15 @@ var listenCommand = new import_commander5.Command("listen").description("Forward
1172
1431
  });
1173
1432
 
1174
1433
  // src/commands/login.ts
1175
- var import_chalk8 = __toESM(require("chalk"));
1434
+ var import_chalk7 = __toESM(require("chalk"));
1176
1435
  var import_commander6 = require("commander");
1177
- var loginCommand = new import_commander6.Command("login").description("Authenticate with Commet").action(async () => {
1436
+ var loginCommand = new import_commander6.Command("login").description(
1437
+ "Authenticate with Commet via browser. Opens a device-code flow \u2014 you confirm in the browser and the CLI stores your token locally at ~/.commet/auth.json."
1438
+ ).action(async () => {
1178
1439
  if (authExists()) {
1179
- console.log(import_chalk8.default.yellow("\u26A0 You are already logged in."));
1440
+ console.log(import_chalk7.default.yellow("\u26A0 You are already logged in."));
1180
1441
  console.log(
1181
- import_chalk8.default.dim(
1442
+ import_chalk7.default.dim(
1182
1443
  "Run `commet logout` first if you want to login with a different account."
1183
1444
  )
1184
1445
  );
@@ -1186,9 +1447,9 @@ var loginCommand = new import_commander6.Command("login").description("Authentic
1186
1447
  }
1187
1448
  const success = await performLogin();
1188
1449
  if (success) {
1189
- console.log(import_chalk8.default.green("\n\u2713 Authentication complete"));
1450
+ console.log(import_chalk7.default.green("\n\u2713 Authentication complete"));
1190
1451
  console.log(
1191
- import_chalk8.default.dim(
1452
+ import_chalk7.default.dim(
1192
1453
  "\nNext steps:\n 1. Run `commet link` to connect a project\n 2. Run `commet pull` to generate types\n"
1193
1454
  )
1194
1455
  );
@@ -1196,134 +1457,93 @@ var loginCommand = new import_commander6.Command("login").description("Authentic
1196
1457
  });
1197
1458
 
1198
1459
  // src/commands/logout.ts
1199
- var import_chalk9 = __toESM(require("chalk"));
1460
+ var import_chalk8 = __toESM(require("chalk"));
1200
1461
  var import_commander7 = require("commander");
1201
- var logoutCommand = new import_commander7.Command("logout").description("Log out of Commet").action(async () => {
1462
+ var logoutCommand = new import_commander7.Command("logout").description(
1463
+ "Log out and remove stored credentials from ~/.commet/auth.json."
1464
+ ).action(async () => {
1202
1465
  if (!authExists()) {
1203
- console.log(import_chalk9.default.yellow("\u26A0 You are not logged in."));
1466
+ console.log(import_chalk8.default.yellow("\u26A0 You are not logged in."));
1204
1467
  return;
1205
1468
  }
1206
1469
  clearAuth();
1207
- console.log(import_chalk9.default.green("\u2713 Successfully logged out"));
1470
+ console.log(import_chalk8.default.green("\u2713 Successfully logged out"));
1208
1471
  });
1209
1472
 
1210
- // src/commands/pull.ts
1211
- var fs5 = __toESM(require("fs"));
1212
- var path5 = __toESM(require("path"));
1213
- var import_prompts3 = require("@inquirer/prompts");
1214
- var import_chalk11 = __toESM(require("chalk"));
1473
+ // src/commands/orgs.ts
1474
+ var import_chalk9 = __toESM(require("chalk"));
1215
1475
  var import_commander8 = require("commander");
1216
1476
  var import_ora5 = __toESM(require("ora"));
1477
+ var orgsCommand = new import_commander8.Command("orgs").description(
1478
+ "List all organizations you have access to. Shows name, slug, mode (live/sandbox), and which one is currently linked."
1479
+ ).option("--json", "Output as JSON array").addHelpText(
1480
+ "after",
1481
+ `
1482
+ Examples:
1483
+ $ commet orgs Show orgs with current selection marked
1484
+ $ commet orgs --json JSON array for agent/CI use
1217
1485
 
1218
- // src/utils/config-loader.ts
1219
- var fs4 = __toESM(require("fs"));
1220
- var path4 = __toESM(require("path"));
1221
- var import_jiti = require("jiti");
1222
- var CONFIG_NAMES = [
1223
- "commet.config.ts",
1224
- "commet.config.js",
1225
- "commet.config.mjs"
1226
- ];
1227
- function findConfigFile(cwd) {
1228
- for (const name of CONFIG_NAMES) {
1229
- const fullPath = path4.resolve(cwd, name);
1230
- if (fs4.existsSync(fullPath)) {
1231
- return fullPath;
1486
+ The slug shown here is what you pass to 'commet link --org <slug>'.
1487
+ `
1488
+ ).action(async (options) => {
1489
+ const jsonMode = options.json;
1490
+ if (!authExists()) {
1491
+ if (jsonMode) {
1492
+ console.log(JSON.stringify({ error: "Not authenticated" }));
1493
+ } else {
1494
+ console.log(import_chalk9.default.red("\u2717 Not authenticated"));
1495
+ console.log(import_chalk9.default.dim("Run `commet login` first"));
1232
1496
  }
1497
+ process.exit(1);
1233
1498
  }
1234
- return null;
1235
- }
1236
- async function loadBillingConfig(cwd) {
1237
- const configPath = findConfigFile(cwd);
1238
- if (!configPath) {
1239
- throw new Error(
1240
- `No commet.config.ts found in ${cwd}. Create one with defineConfig() or run 'commet pull' to generate it.`
1241
- );
1499
+ const spinner = jsonMode ? null : (0, import_ora5.default)("Fetching organizations...").start();
1500
+ const result = await apiRequest(
1501
+ `${BASE_URL}/api/cli/organizations`
1502
+ );
1503
+ if (result.error || !result.data) {
1504
+ if (jsonMode) {
1505
+ console.log(JSON.stringify({ error: result.error }));
1506
+ } else {
1507
+ spinner?.fail("Failed to fetch organizations");
1508
+ console.error(import_chalk9.default.red("Error:"), result.error);
1509
+ }
1510
+ process.exit(1);
1242
1511
  }
1243
- const jiti = (0, import_jiti.createJiti)(configPath, { interopDefault: true });
1244
- const mod = await jiti.import(configPath);
1245
- if (!mod || typeof mod !== "object") {
1246
- throw new Error(`${configPath}: failed to load config module`);
1512
+ spinner?.stop();
1513
+ const { organizations } = result.data;
1514
+ if (jsonMode) {
1515
+ console.log(JSON.stringify(organizations));
1516
+ return;
1247
1517
  }
1248
- const moduleRecord = mod;
1249
- if (!moduleRecord.default) {
1250
- throw new Error(
1251
- `${configPath}: must use \`export default defineConfig({...})\``
1518
+ if (organizations.length === 0) {
1519
+ console.log(import_chalk9.default.yellow("\u26A0 No organizations found"));
1520
+ console.log(
1521
+ import_chalk9.default.dim("Create an organization at https://commet.co first")
1252
1522
  );
1523
+ return;
1253
1524
  }
1254
- const config = moduleRecord.default;
1255
- validateConfig(config, configPath);
1256
- return { config, configPath };
1257
- }
1258
- var VALID_FEATURE_TYPES = /* @__PURE__ */ new Set(["boolean", "usage", "seats"]);
1259
- var VALID_INTERVALS = /* @__PURE__ */ new Set([
1260
- "weekly",
1261
- "monthly",
1262
- "quarterly",
1263
- "yearly",
1264
- "one_time"
1265
- ]);
1266
- function validateConfig(config, configPath) {
1267
- if (!config || typeof config !== "object") {
1268
- throw new Error(`${configPath}: config must be an object`);
1269
- }
1270
- if (!config.features || typeof config.features !== "object") {
1271
- throw new Error(`${configPath}: config.features must be an object`);
1272
- }
1273
- if (!config.plans || typeof config.plans !== "object") {
1274
- throw new Error(`${configPath}: config.plans must be an object`);
1275
- }
1276
- for (const [code, feature] of Object.entries(config.features)) {
1277
- if (!feature.name || typeof feature.name !== "string") {
1278
- throw new Error(`Feature "${code}": name is required`);
1279
- }
1280
- if (!VALID_FEATURE_TYPES.has(feature.type)) {
1281
- throw new Error(
1282
- `Feature "${code}": type must be one of: boolean, usage, seats`
1283
- );
1284
- }
1285
- }
1286
- for (const [code, plan] of Object.entries(config.plans)) {
1287
- if (!plan.name || typeof plan.name !== "string") {
1288
- throw new Error(`Plan "${code}": name is required`);
1289
- }
1290
- if (!Array.isArray(plan.prices)) {
1291
- throw new Error(`Plan "${code}": prices must be an array`);
1292
- }
1293
- for (const price of plan.prices) {
1294
- if (!VALID_INTERVALS.has(price.interval)) {
1295
- throw new Error(
1296
- `Plan "${code}": price interval "${price.interval}" is not valid`
1297
- );
1298
- }
1299
- if (typeof price.amount !== "number") {
1300
- throw new Error(`Plan "${code}": price amount must be a number`);
1301
- }
1302
- }
1303
- if (plan.prices.length > 0) {
1304
- if (!plan.defaultInterval) {
1305
- throw new Error(
1306
- `Plan "${code}": defaultInterval is required when prices are defined`
1307
- );
1308
- }
1309
- const priceIntervals = new Set(plan.prices.map((p) => p.interval));
1310
- if (!priceIntervals.has(plan.defaultInterval)) {
1311
- throw new Error(
1312
- `Plan "${code}": defaultInterval "${plan.defaultInterval}" does not match any price interval (${[...priceIntervals].join(", ")})`
1313
- );
1314
- }
1315
- }
1316
- if (plan.features) {
1317
- for (const featureCode of Object.keys(plan.features)) {
1318
- if (!config.features[featureCode]) {
1319
- throw new Error(
1320
- `Plan "${code}": references feature "${featureCode}" which is not defined in config.features`
1321
- );
1322
- }
1323
- }
1324
- }
1525
+ const currentProject = loadProjectConfig();
1526
+ console.log(import_chalk9.default.bold(`
1527
+ Organizations (${organizations.length})
1528
+ `));
1529
+ for (const org of organizations) {
1530
+ const isCurrent = currentProject?.orgId === org.id;
1531
+ const marker = isCurrent ? import_chalk9.default.green("\u25CF") : import_chalk9.default.dim("\u25CB");
1532
+ const mode = org.mode === "live" ? import_chalk9.default.green(org.mode) : import_chalk9.default.yellow(org.mode);
1533
+ console.log(
1534
+ ` ${marker} ${org.name} ${import_chalk9.default.dim(`(${org.slug})`)} ${mode}`
1535
+ );
1325
1536
  }
1326
- }
1537
+ console.log("");
1538
+ });
1539
+
1540
+ // src/commands/pull.ts
1541
+ var fs5 = __toESM(require("fs"));
1542
+ var path5 = __toESM(require("path"));
1543
+ var import_prompts3 = require("@inquirer/prompts");
1544
+ var import_chalk11 = __toESM(require("chalk"));
1545
+ var import_commander9 = require("commander");
1546
+ var import_ora6 = __toESM(require("ora"));
1327
1547
 
1328
1548
  // src/utils/diff.ts
1329
1549
  var import_chalk10 = __toESM(require("chalk"));
@@ -1532,7 +1752,18 @@ function generateConfigFile(features, plans) {
1532
1752
  }
1533
1753
 
1534
1754
  // src/commands/pull.ts
1535
- var pullCommand = new import_commander8.Command("pull").description("Pull config from Commet and generate commet.config.ts").option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show diff without applying changes").option("--json", "Output structured JSON (no colors, no prompts)").action(async (options) => {
1755
+ var pullCommand = new import_commander9.Command("pull").description(
1756
+ "Fetch your billing config from Commet and generate (or update) commet.config.ts with features and plans."
1757
+ ).option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show what would change without writing any files").option("--json", "Output structured JSON (no colors, no prompts)").addHelpText(
1758
+ "after",
1759
+ `
1760
+ Examples:
1761
+ $ commet pull Interactive \u2014 shows diff, asks to confirm
1762
+ $ commet pull --dry-run Preview changes without applying
1763
+ $ commet pull --yes Apply without confirmation
1764
+ $ commet pull --json --yes Agent/CI \u2014 structured JSON, no prompts
1765
+ `
1766
+ ).action(async (options) => {
1536
1767
  const jsonMode = options.json;
1537
1768
  if (!authExists()) {
1538
1769
  if (jsonMode) {
@@ -1563,7 +1794,7 @@ var pullCommand = new import_commander8.Command("pull").description("Pull config
1563
1794
  }
1564
1795
  process.exit(1);
1565
1796
  }
1566
- const spinner = jsonMode ? null : (0, import_ora5.default)("Fetching config from remote...").start();
1797
+ const spinner = jsonMode ? null : (0, import_ora6.default)("Fetching config from remote...").start();
1567
1798
  const result = await apiRequest(
1568
1799
  `${BASE_URL}/api/cli/pull?orgId=${projectConfig.orgId}`
1569
1800
  );
@@ -1778,9 +2009,24 @@ Would create commet.config.ts (${features.length} features, ${plans.length} plan
1778
2009
  // src/commands/push.ts
1779
2010
  var import_prompts4 = require("@inquirer/prompts");
1780
2011
  var import_chalk12 = __toESM(require("chalk"));
1781
- var import_commander9 = require("commander");
1782
- var import_ora6 = __toESM(require("ora"));
1783
- var pushCommand = new import_commander9.Command("push").description("Push commet.config.ts to your Commet organization").option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show diff without applying changes").option("--json", "Output structured JSON (no colors, no prompts)").action(async (options) => {
2012
+ var import_commander10 = require("commander");
2013
+ var import_ora7 = __toESM(require("ora"));
2014
+ var pushCommand = new import_commander10.Command("push").description(
2015
+ "Push your local commet.config.ts to Commet. Creates or updates features and plans to match your config file."
2016
+ ).option("-y, --yes", "Skip confirmation prompt").option("--dry-run", "Show what would change without pushing").option("--json", "Output structured JSON (no colors, no prompts)").addHelpText(
2017
+ "after",
2018
+ `
2019
+ Examples:
2020
+ $ commet push Interactive \u2014 shows diff, asks to confirm
2021
+ $ commet push --dry-run Preview what would change on remote
2022
+ $ commet push --yes Push without confirmation
2023
+ $ commet push --json --yes Agent/CI \u2014 structured JSON, no prompts
2024
+
2025
+ Notes:
2026
+ Feature types (boolean, usage, seats) cannot be changed via push.
2027
+ Resources in remote but not in your config are left as-is (not deleted).
2028
+ `
2029
+ ).action(async (options) => {
1784
2030
  const jsonMode = options.json;
1785
2031
  if (!authExists()) {
1786
2032
  if (jsonMode) {
@@ -1811,7 +2057,7 @@ var pushCommand = new import_commander9.Command("push").description("Push commet
1811
2057
  }
1812
2058
  process.exit(1);
1813
2059
  }
1814
- const loadSpinner = jsonMode ? null : (0, import_ora6.default)("Loading commet.config.ts...").start();
2060
+ const loadSpinner = jsonMode ? null : (0, import_ora7.default)("Loading commet.config.ts...").start();
1815
2061
  const loaded = await loadBillingConfig(process.cwd()).catch(
1816
2062
  (error) => {
1817
2063
  const message = error instanceof Error ? error.message : String(error);
@@ -1827,7 +2073,7 @@ var pushCommand = new import_commander9.Command("push").description("Push commet
1827
2073
  if (!loaded) process.exit(1);
1828
2074
  const { config, configPath } = loaded;
1829
2075
  loadSpinner?.succeed(`Loaded ${configPath}`);
1830
- const fetchSpinner = jsonMode ? null : (0, import_ora6.default)("Fetching remote state...").start();
2076
+ const fetchSpinner = jsonMode ? null : (0, import_ora7.default)("Fetching remote state...").start();
1831
2077
  const remoteResult = await apiRequest(
1832
2078
  `${BASE_URL}/api/cli/pull?orgId=${projectConfig.orgId}`
1833
2079
  );
@@ -1903,7 +2149,7 @@ var pushCommand = new import_commander9.Command("push").description("Push commet
1903
2149
  return;
1904
2150
  }
1905
2151
  }
1906
- const pushSpinner = jsonMode ? null : (0, import_ora6.default)("Pushing config...").start();
2152
+ const pushSpinner = jsonMode ? null : (0, import_ora7.default)("Pushing config...").start();
1907
2153
  const pushResult = await apiRequest(
1908
2154
  `${BASE_URL}/api/cli/push`,
1909
2155
  {
@@ -1974,65 +2220,94 @@ var pushCommand = new import_commander9.Command("push").description("Push commet
1974
2220
  // src/commands/switch.ts
1975
2221
  var import_prompts5 = require("@inquirer/prompts");
1976
2222
  var import_chalk13 = __toESM(require("chalk"));
1977
- var import_commander10 = require("commander");
1978
- var import_ora7 = __toESM(require("ora"));
1979
- var switchCommand = new import_commander10.Command("switch").description("Switch to a different organization").action(async () => {
2223
+ var import_commander11 = require("commander");
2224
+ var import_ora8 = __toESM(require("ora"));
2225
+ var switchCommand = new import_commander11.Command("switch").description(
2226
+ "Switch this project to a different organization. Updates .commet/config.json with the new org."
2227
+ ).option(
2228
+ "--org <slug-or-id>",
2229
+ "Organization slug or ID \u2014 skips interactive selection"
2230
+ ).addHelpText(
2231
+ "after",
2232
+ `
2233
+ Examples:
2234
+ $ commet switch Interactive \u2014 choose from a list
2235
+ $ commet switch --org acme Non-interactive \u2014 match by slug or ID
2236
+
2237
+ Use 'commet orgs' to see available organizations and their slugs.
2238
+ After switching, run 'commet pull' to update your config file.
2239
+ `
2240
+ ).action(async (options) => {
1980
2241
  if (!authExists()) {
1981
2242
  console.log(import_chalk13.default.red("\u2717 Not authenticated"));
1982
2243
  console.log(import_chalk13.default.dim("Run `commet login` first"));
1983
- return;
2244
+ process.exit(1);
1984
2245
  }
1985
2246
  if (!projectConfigExists()) {
1986
2247
  console.log(import_chalk13.default.yellow("\u26A0 Project not linked"));
1987
2248
  console.log(
1988
2249
  import_chalk13.default.dim("Run `commet link` first to connect to an organization")
1989
2250
  );
1990
- return;
2251
+ process.exit(1);
1991
2252
  }
1992
- const spinner = (0, import_ora7.default)("Fetching organizations...").start();
2253
+ const spinner = (0, import_ora8.default)("Fetching organizations...").start();
1993
2254
  const result = await apiRequest(
1994
2255
  `${BASE_URL}/api/cli/organizations`
1995
2256
  );
1996
2257
  if (result.error || !result.data) {
1997
2258
  spinner.fail("Failed to fetch organizations");
1998
2259
  console.error(import_chalk13.default.red("Error:"), result.error);
1999
- return;
2260
+ process.exit(1);
2000
2261
  }
2001
2262
  const { organizations } = result.data;
2002
2263
  if (organizations.length === 0) {
2003
2264
  spinner.stop();
2004
2265
  console.log(import_chalk13.default.yellow("\u26A0 No organizations found"));
2005
- return;
2266
+ process.exit(1);
2006
2267
  }
2007
2268
  spinner.stop();
2008
- let orgId;
2009
- try {
2010
- orgId = await (0, import_prompts5.select)({
2011
- message: "Select organization:",
2012
- choices: organizations.map((org) => ({
2013
- name: `${org.name} ${import_chalk13.default.dim(`(${org.slug}) \xB7 ${org.mode}`)}`,
2014
- value: org.id
2015
- })),
2016
- theme: promptTheme
2017
- });
2018
- } catch (_error) {
2019
- console.log(import_chalk13.default.yellow("\n\u26A0 Switch cancelled"));
2020
- return;
2021
- }
2022
- const selectedOrg = organizations.find((org) => org.id === orgId);
2023
- if (!selectedOrg) {
2024
- console.log(import_chalk13.default.red("\u2717 Organization not found"));
2025
- return;
2269
+ let selectedOrg;
2270
+ if (options.org) {
2271
+ selectedOrg = organizations.find(
2272
+ (org) => org.slug === options.org || org.id === options.org
2273
+ );
2274
+ if (!selectedOrg) {
2275
+ console.log(import_chalk13.default.red(`\u2717 Organization "${options.org}" not found`));
2276
+ console.log(import_chalk13.default.dim("\nAvailable organizations:"));
2277
+ for (const org of organizations) {
2278
+ console.log(import_chalk13.default.dim(` ${org.slug} (${org.mode})`));
2279
+ }
2280
+ process.exit(1);
2281
+ }
2282
+ } else {
2283
+ let orgId;
2284
+ try {
2285
+ orgId = await (0, import_prompts5.select)({
2286
+ message: "Select organization:",
2287
+ choices: organizations.map((org) => ({
2288
+ name: `${org.name} ${import_chalk13.default.dim(`(${org.slug}) \xB7 ${org.mode}`)}`,
2289
+ value: org.id
2290
+ })),
2291
+ theme: promptTheme
2292
+ });
2293
+ } catch (_error) {
2294
+ console.log(import_chalk13.default.yellow("\n\u26A0 Switch cancelled"));
2295
+ return;
2296
+ }
2297
+ selectedOrg = organizations.find((org) => org.id === orgId);
2298
+ if (!selectedOrg) {
2299
+ console.log(import_chalk13.default.red("\u2717 Organization not found"));
2300
+ process.exit(1);
2301
+ }
2026
2302
  }
2027
2303
  saveProjectConfig({
2028
2304
  orgId: selectedOrg.id,
2029
2305
  orgName: selectedOrg.name,
2030
2306
  mode: selectedOrg.mode
2031
2307
  });
2032
- console.log(import_chalk13.default.green("\n\u2713 Switched organization successfully!"));
2308
+ console.log(import_chalk13.default.green("\n\u2713 Switched organization"));
2033
2309
  console.log(
2034
- import_chalk13.default.dim(`
2035
- Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
2310
+ import_chalk13.default.dim(`Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
2036
2311
  );
2037
2312
  console.log(
2038
2313
  import_chalk13.default.dim(
@@ -2043,8 +2318,10 @@ Organization: ${selectedOrg.name} \xB7 ${selectedOrg.mode}`)
2043
2318
 
2044
2319
  // src/commands/unlink.ts
2045
2320
  var import_chalk14 = __toESM(require("chalk"));
2046
- var import_commander11 = require("commander");
2047
- var unlinkCommand = new import_commander11.Command("unlink").description("Unlink this project from Commet").action(async () => {
2321
+ var import_commander12 = require("commander");
2322
+ var unlinkCommand = new import_commander12.Command("unlink").description(
2323
+ "Unlink this project from its organization. Removes the .commet/ directory. Does not delete anything on remote."
2324
+ ).action(async () => {
2048
2325
  if (!projectConfigExists()) {
2049
2326
  console.log(
2050
2327
  import_chalk14.default.yellow("\u26A0 This project is not linked to any organization")
@@ -2059,48 +2336,40 @@ var unlinkCommand = new import_commander11.Command("unlink").description("Unlink
2059
2336
  );
2060
2337
  });
2061
2338
 
2062
- // src/commands/whoami.ts
2063
- var import_chalk15 = __toESM(require("chalk"));
2064
- var import_commander12 = require("commander");
2065
- var whoamiCommand = new import_commander12.Command("whoami").description("Display current authentication and project status").action(async () => {
2066
- if (!authExists()) {
2067
- console.log(import_chalk15.default.yellow("\u26A0 Not logged in"));
2068
- console.log(import_chalk15.default.dim("Run `commet login` to authenticate"));
2069
- return;
2070
- }
2071
- console.log(import_chalk15.default.green("\u2713 Logged in"));
2072
- const projectConfig = loadProjectConfig();
2073
- if (projectConfig) {
2074
- console.log(import_chalk15.default.bold("\nProject:"));
2075
- console.log(import_chalk15.default.dim("Organization:"), projectConfig.orgName);
2076
- console.log(import_chalk15.default.dim("Mode:"), projectConfig.mode);
2077
- } else {
2078
- console.log(import_chalk15.default.yellow("\n\u26A0 No project linked"));
2079
- console.log(
2080
- import_chalk15.default.dim(
2081
- "Run `commet link` to connect this directory to an organization"
2082
- )
2083
- );
2084
- }
2085
- });
2086
-
2087
2339
  // src/index.ts
2088
2340
  var program = new import_commander13.Command();
2089
2341
  program.name("commet").description(
2090
- "Commet CLI - Manage your billing platform from the command line"
2091
- ).version(package_default.version);
2342
+ "Commet CLI \u2014 billing infrastructure as code.\nManage features, plans, and billing config from the command line."
2343
+ ).version(package_default.version).addHelpText(
2344
+ "after",
2345
+ `
2346
+ Workflow:
2347
+ $ commet pull Sync remote \u2192 commet.config.ts
2348
+ $ commet push Push local changes \u2192 remote
2349
+ $ commet list features See what's configured
2350
+
2351
+ For agents and CI:
2352
+ $ commet agent-info JSON with status + every command's usage
2353
+ $ commet pull --json --yes Structured output, no prompts
2354
+ $ commet orgs --json List orgs as JSON
2355
+ $ commet link --org <slug> Link without interactive selection
2356
+
2357
+ Run commet <command> --help for detailed usage and examples.
2358
+ `
2359
+ );
2092
2360
  program.addCommand(createCommand);
2093
2361
  program.addCommand(loginCommand);
2094
2362
  program.addCommand(logoutCommand);
2095
- program.addCommand(whoamiCommand);
2096
2363
  program.addCommand(linkCommand);
2097
2364
  program.addCommand(unlinkCommand);
2098
2365
  program.addCommand(switchCommand);
2099
- program.addCommand(infoCommand);
2366
+ program.addCommand(agentInfoCommand);
2367
+ program.addCommand(orgsCommand);
2100
2368
  program.addCommand(pushCommand);
2101
2369
  program.addCommand(pullCommand);
2102
2370
  program.addCommand(listCommand);
2103
2371
  program.addCommand(listenCommand);
2372
+ program.showSuggestionAfterError();
2104
2373
  program.exitOverride();
2105
2374
  try {
2106
2375
  program.parse(process.argv);
@@ -2110,10 +2379,69 @@ try {
2110
2379
  if (code === "commander.version" || code === "commander.help" || code === "commander.helpDisplayed") {
2111
2380
  process.exit(0);
2112
2381
  }
2113
- console.error(import_chalk16.default.red("Error:"), error.message);
2382
+ console.error(import_chalk15.default.red("Error:"), error.message);
2114
2383
  }
2115
2384
  process.exit(1);
2116
2385
  }
2117
2386
  if (!process.argv.slice(2).length) {
2118
- program.outputHelp();
2387
+ printDefaultScreen();
2388
+ }
2389
+ function printDefaultScreen() {
2390
+ const version = import_chalk15.default.dim(`v${package_default.version}`);
2391
+ console.log(`
2392
+ ${commetColor.bold("Commet")} ${version}`);
2393
+ console.log(import_chalk15.default.dim(" Billing infrastructure as code\n"));
2394
+ const authenticated = authExists();
2395
+ const projectConfig = projectConfigExists() ? loadProjectConfig() : null;
2396
+ const configFile = findConfigFile(process.cwd());
2397
+ if (authenticated) {
2398
+ console.log(import_chalk15.default.green(" \u2713 Authenticated"));
2399
+ } else {
2400
+ console.log(
2401
+ ` ${import_chalk15.default.yellow("\u26A0")} Not logged in ${import_chalk15.default.dim("\u2192 commet login")}`
2402
+ );
2403
+ }
2404
+ if (projectConfig) {
2405
+ console.log(
2406
+ import_chalk15.default.green(
2407
+ ` \u2713 ${projectConfig.orgName} ${import_chalk15.default.dim(`(${projectConfig.mode})`)}`
2408
+ )
2409
+ );
2410
+ console.log(import_chalk15.default.dim(` ${projectConfig.orgId}`));
2411
+ } else if (authenticated) {
2412
+ console.log(
2413
+ ` ${import_chalk15.default.yellow("\u26A0")} No project linked ${import_chalk15.default.dim("\u2192 commet link")}`
2414
+ );
2415
+ }
2416
+ if (configFile) {
2417
+ const fileName = configFile.split("/").pop();
2418
+ console.log(import_chalk15.default.green(` \u2713 ${fileName}`));
2419
+ } else if (projectConfig) {
2420
+ console.log(
2421
+ ` ${import_chalk15.default.yellow("\u26A0")} No config file ${import_chalk15.default.dim("\u2192 commet pull")}`
2422
+ );
2423
+ }
2424
+ const cmd = (name) => commetColor(name.padEnd(22));
2425
+ const dim = import_chalk15.default.dim;
2426
+ console.log(dim("\n Config"));
2427
+ console.log(` ${cmd("pull")}${dim("Sync remote \u2192 commet.config.ts")}`);
2428
+ console.log(` ${cmd("push")}${dim("Sync commet.config.ts \u2192 remote")}`);
2429
+ console.log(
2430
+ ` ${cmd("list <type>")}${dim("List features, plans, or seats")}`
2431
+ );
2432
+ console.log(dim("\n Development"));
2433
+ console.log(` ${cmd("listen <url>")}${dim("Forward webhooks locally")}`);
2434
+ console.log(dim("\n Project"));
2435
+ console.log(` ${cmd("link")}${dim("Link to an organization")}`);
2436
+ console.log(` ${cmd("switch")}${dim("Switch organization")}`);
2437
+ console.log(` ${cmd("orgs")}${dim("List organizations")}`);
2438
+ console.log(dim("\n Setup"));
2439
+ console.log(` ${cmd("create")}${dim("Scaffold a new Commet app")}`);
2440
+ console.log(` ${cmd("login")}${dim("Authenticate")}`);
2441
+ console.log(` ${cmd("logout")}${dim("Log out")}`);
2442
+ console.log(
2443
+ dim(
2444
+ "\n commet --help for full reference \xB7 commet agent-info for agents/CI\n"
2445
+ )
2446
+ );
2119
2447
  }