calabasas 0.4.0 → 0.5.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 +284 -85
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2236,7 +2236,8 @@ export default defineCalabasas({
2236
2236
  `;
2237
2237
  async function init() {
2238
2238
  const convexDir = path.resolve(process.cwd(), "convex");
2239
- const configPath = path.join(convexDir, "calabasas.config.ts");
2239
+ const calabasasDir = path.join(convexDir, "calabasas");
2240
+ const configPath = path.join(calabasasDir, "config.ts");
2240
2241
  p3.intro("calabasas init");
2241
2242
  if (!fs.existsSync(convexDir)) {
2242
2243
  p3.cancel(`convex/ directory not found.
@@ -2246,15 +2247,18 @@ Run \`npx convex dev\` to initialize a new Convex project.`);
2246
2247
  process.exit(1);
2247
2248
  }
2248
2249
  if (fs.existsSync(configPath)) {
2249
- p3.cancel(`convex/calabasas.config.ts already exists.
2250
+ p3.cancel(`convex/calabasas/config.ts already exists.
2250
2251
 
2251
2252
  Delete the existing file if you want to start fresh:
2252
- rm convex/calabasas.config.ts`);
2253
+ rm convex/calabasas/config.ts`);
2253
2254
  process.exit(1);
2254
2255
  }
2256
+ if (!fs.existsSync(calabasasDir)) {
2257
+ fs.mkdirSync(calabasasDir, { recursive: true });
2258
+ }
2255
2259
  fs.writeFileSync(configPath, CONFIG_TEMPLATE);
2256
- p3.note("1. Edit convex/calabasas.config.ts to configure sync and events\n2. Run `calabasas generate` to generate type-safe handlers\n3. Run `calabasas push` to sync config with Calabasas", "Next steps");
2257
- p3.outro("Created convex/calabasas.config.ts");
2260
+ p3.note("1. Edit convex/calabasas/config.ts to configure sync and events\n2. Run `calabasas generate` to generate type-safe handlers\n3. Run `calabasas push` to sync config with Calabasas", "Next steps");
2261
+ p3.outro("Created convex/calabasas/config.ts");
2258
2262
  }
2259
2263
 
2260
2264
  // src/commands/push.ts
@@ -2321,7 +2325,7 @@ async function push(options) {
2321
2325
  const configPath = path3.resolve(process.cwd(), options.config);
2322
2326
  if (!fs3.existsSync(configPath)) {
2323
2327
  console.error(`Error: Config file not found: ${configPath}`);
2324
- console.error("\nRun `calabasas init` to create a calabasas.config.ts file.");
2328
+ console.error("\nRun `calabasas init` to create a config file.");
2325
2329
  process.exit(1);
2326
2330
  }
2327
2331
  if (!isAuthenticated(env)) {
@@ -2840,7 +2844,7 @@ export const syncGuildAdmins = mutation({
2840
2844
  if (!expectedSecret || secret !== expectedSecret) {
2841
2845
  throw new Error("Invalid secret");
2842
2846
  }
2843
- await ctx.runMutation(internal.calabasas.sync._syncGuildAdmins, { data });
2847
+ await ctx.runMutation(internal.calabasas._generated.sync._syncGuildAdmins, { data });
2844
2848
  return null;
2845
2849
  },
2846
2850
  });`;
@@ -2864,7 +2868,7 @@ export const sync${capitalType} = mutation({
2864
2868
  if (!expectedSecret || secret !== expectedSecret) {
2865
2869
  throw new Error("Invalid secret");
2866
2870
  }
2867
- await ctx.runMutation(internal.calabasas.sync._sync${capitalType}, { data, operation });
2871
+ await ctx.runMutation(internal.calabasas._generated.sync._sync${capitalType}, { data, operation });
2868
2872
  return null;
2869
2873
  },
2870
2874
  });`;
@@ -2920,7 +2924,7 @@ function generateSyncFile(sync) {
2920
2924
  * updated, run \`calabasas generate\` to regenerate it from the config.
2921
2925
  * Editing this file directly will cause your changes to be overwritten.
2922
2926
  *
2923
- * No sync handlers configured - enable sync options in calabasas.config.ts
2927
+ * No sync handlers configured - enable sync options in convex/calabasas/config.ts
2924
2928
  */
2925
2929
 
2926
2930
  export {};
@@ -2936,8 +2940,8 @@ export {};
2936
2940
  * Editing this file directly will cause your changes to be overwritten.
2937
2941
  */
2938
2942
 
2939
- import { mutation, internalMutation } from "../_generated/server";
2940
- import { internal } from "../_generated/api";
2943
+ import { mutation, internalMutation } from "../../_generated/server";
2944
+ import { internal } from "../../_generated/api";
2941
2945
  import { v } from "convex/values";
2942
2946
 
2943
2947
  // ============================================================================
@@ -3273,9 +3277,9 @@ function generateHandlersCode(eventNames) {
3273
3277
  * Editing this file directly will cause your changes to be overwritten.
3274
3278
  */
3275
3279
 
3276
- import { mutation } from "./_generated/server";
3280
+ import { mutation } from "../../_generated/server";
3277
3281
  import { v } from "convex/values";
3278
- import type { MutationCtx } from "./_generated/server";
3282
+ import type { MutationCtx } from "../../_generated/server";
3279
3283
 
3280
3284
  // Event type validators
3281
3285
  ${eventNames.map((name) => `const ${name}Validator = ${generateValidatorForEvent(name)};`).join(`
@@ -3496,17 +3500,27 @@ export const discord = {
3496
3500
  }
3497
3501
  async function generate(options) {
3498
3502
  const convexDir = path4.resolve(process.cwd(), "convex");
3499
- const configPath = path4.join(convexDir, "calabasas.config.ts");
3503
+ const calabasasDir = path4.join(convexDir, "calabasas");
3504
+ const configPath = path4.join(calabasasDir, "config.ts");
3505
+ const legacyConfigPath = path4.join(convexDir, "calabasas.config.ts");
3500
3506
  p5.intro("calabasas generate");
3501
3507
  let config = { sync: {}, events: {} };
3502
3508
  if (fs4.existsSync(configPath)) {
3503
3509
  config = await parseConfigFile(configPath);
3510
+ } else if (fs4.existsSync(legacyConfigPath)) {
3511
+ p5.log.warn("Found config at legacy path convex/calabasas.config.ts");
3512
+ p5.log.info("Run `calabasas migrate dedicated-folder` to move files to the new layout.");
3513
+ config = await parseConfigFile(legacyConfigPath);
3504
3514
  } else {
3505
- p5.log.warn("No calabasas.config.ts found, generating with default settings...");
3515
+ p5.log.warn("No config found, generating with default settings...");
3506
3516
  p5.log.info("Run `calabasas init` to create a config file.");
3507
3517
  }
3508
3518
  const s = p5.spinner();
3509
3519
  s.start("Generating files...");
3520
+ const generatedDir = path4.join(calabasasDir, "_generated");
3521
+ if (!fs4.existsSync(generatedDir)) {
3522
+ fs4.mkdirSync(generatedDir, { recursive: true });
3523
+ }
3510
3524
  const eventHandlersPath = path4.resolve(process.cwd(), options.output);
3511
3525
  const eventHandlersCode = generateEventHandlersFile(config.events ?? {});
3512
3526
  fs4.writeFileSync(eventHandlersPath, eventHandlersCode);
@@ -3514,23 +3528,19 @@ async function generate(options) {
3514
3528
  const syncConfig = config.sync ?? {};
3515
3529
  const hasSyncEnabled = syncConfig.guilds || syncConfig.channels || syncConfig.roles || syncConfig.members;
3516
3530
  if (hasSyncEnabled) {
3517
- const calabasasDir = path4.join(convexDir, "calabasas");
3518
- if (!fs4.existsSync(calabasasDir)) {
3519
- fs4.mkdirSync(calabasasDir, { recursive: true });
3520
- }
3521
- const schemaPath = path4.join(calabasasDir, "schema.ts");
3531
+ const schemaPath = path4.join(generatedDir, "schema.ts");
3522
3532
  const schemaCode = generateSchemaFile(syncConfig);
3523
3533
  fs4.writeFileSync(schemaPath, schemaCode);
3524
- generated.push("convex/calabasas/schema.ts");
3525
- const syncPath = path4.join(calabasasDir, "sync.ts");
3534
+ generated.push("convex/calabasas/_generated/schema.ts");
3535
+ const syncPath = path4.join(generatedDir, "sync.ts");
3526
3536
  const syncCode = generateSyncFile(syncConfig);
3527
3537
  fs4.writeFileSync(syncPath, syncCode);
3528
- generated.push("convex/calabasas/sync.ts");
3538
+ generated.push("convex/calabasas/_generated/sync.ts");
3529
3539
  }
3530
- const actionsPath = path4.join(convexDir, "discord.actions.generated.ts");
3540
+ const actionsPath = path4.join(generatedDir, "discord.actions.ts");
3531
3541
  const actionsCode = generateActionsFile();
3532
3542
  fs4.writeFileSync(actionsPath, actionsCode);
3533
- generated.push("convex/discord.actions.generated.ts");
3543
+ generated.push("convex/calabasas/_generated/discord.actions.ts");
3534
3544
  s.stop(`Generated ${generated.length} file${generated.length > 1 ? "s" : ""}`);
3535
3545
  p5.note(generated.join(`
3536
3546
  `), "Generated files");
@@ -3547,14 +3557,14 @@ async function generate(options) {
3547
3557
  if (syncConfig.members)
3548
3558
  syncExports.push("syncMember");
3549
3559
  p5.note(`1. Add tables to your convex/schema.ts:
3550
- import { calabasasTables } from "./calabasas/schema";
3560
+ import { calabasasTables } from "./calabasas/_generated/schema";
3551
3561
 
3552
3562
  export default defineSchema({
3553
3563
  ...calabasasTables,
3554
3564
  });
3555
3565
 
3556
3566
  2. Re-export sync mutations in convex/discord.ts:
3557
- export { ${syncExports.join(", ")} } from "./calabasas/sync";
3567
+ export { ${syncExports.join(", ")} } from "./calabasas/_generated/sync";
3558
3568
 
3559
3569
  3. Add CALABASAS_SECRET to your Convex environment variables
3560
3570
  4. Create your event handler in convex/discord.ts
@@ -3563,7 +3573,7 @@ async function generate(options) {
3563
3573
  p5.note(`1. Add CALABASAS_SECRET to your Convex environment variables
3564
3574
  2. Create your handler in convex/discord.ts:
3565
3575
 
3566
- import { handleDiscordEvent } from "./discord.generated";
3576
+ import { handleDiscordEvent } from "./calabasas/_generated/discord";
3567
3577
 
3568
3578
  export const receive = handleDiscordEvent({
3569
3579
  messageCreate: async (ctx, event) => {
@@ -5702,9 +5712,10 @@ async function add(componentNames) {
5702
5712
  const cwd = process.cwd();
5703
5713
  const convexDir = path6.resolve(cwd, "convex");
5704
5714
  const calabasasDir = path6.join(convexDir, "calabasas");
5705
- const configPath = path6.join(convexDir, "calabasas.config.ts");
5706
- if (!fs6.existsSync(calabasasDir) || !fs6.existsSync(path6.join(calabasasDir, "schema.ts"))) {
5707
- console.log("Error: convex/calabasas/schema.ts not found.");
5715
+ const configPath = path6.join(calabasasDir, "config.ts");
5716
+ const generatedDir = path6.join(calabasasDir, "_generated");
5717
+ if (!fs6.existsSync(generatedDir) || !fs6.existsSync(path6.join(generatedDir, "schema.ts"))) {
5718
+ console.log("Error: convex/calabasas/_generated/schema.ts not found.");
5708
5719
  console.log("");
5709
5720
  console.log("Run `calabasas init` and `calabasas generate` first to enable sync.");
5710
5721
  return;
@@ -5755,7 +5766,7 @@ async function add(componentNames) {
5755
5766
  const missingSyncTypes = comp.requiredSyncTypes.filter((t) => !enabledSync.has(t));
5756
5767
  if (missingSyncTypes.length > 0) {
5757
5768
  p7.log.warn(`Skipped ${comp.name} — requires sync types not enabled: ${missingSyncTypes.join(", ")}`);
5758
- p7.log.info(`Enable them in calabasas.config.ts and re-run \`calabasas generate\`.`);
5769
+ p7.log.info(`Enable them in convex/calabasas/config.ts and re-run \`calabasas generate\`.`);
5759
5770
  continue;
5760
5771
  }
5761
5772
  if (!fs6.existsSync(componentsDir)) {
@@ -5858,8 +5869,195 @@ ${props}
5858
5869
  }
5859
5870
  }
5860
5871
 
5861
- // src/commands/bot.ts
5872
+ // src/commands/migrate.ts
5873
+ import * as p9 from "@clack/prompts";
5874
+
5875
+ // src/migrations/dedicated-folder.ts
5876
+ import * as fs7 from "fs";
5877
+ import * as path7 from "path";
5862
5878
  import * as p8 from "@clack/prompts";
5879
+ var FILE_MOVES = [
5880
+ ["convex/calabasas.config.ts", "convex/calabasas/config.ts"],
5881
+ ["convex/discord.generated.ts", "convex/calabasas/_generated/discord.ts"],
5882
+ [
5883
+ "convex/discord.actions.generated.ts",
5884
+ "convex/calabasas/_generated/discord.actions.ts"
5885
+ ],
5886
+ ["convex/calabasas/schema.ts", "convex/calabasas/_generated/schema.ts"],
5887
+ ["convex/calabasas/sync.ts", "convex/calabasas/_generated/sync.ts"]
5888
+ ];
5889
+ var IMPORT_REWRITES = [
5890
+ [
5891
+ /from\s+["']\.\/discord\.generated["']/g,
5892
+ `from "./calabasas/_generated/discord"`
5893
+ ],
5894
+ [
5895
+ /from\s+["']\.\/discord\.actions\.generated["']/g,
5896
+ `from "./calabasas/_generated/discord.actions"`
5897
+ ],
5898
+ [
5899
+ /from\s+["']\.\/calabasas\/schema["']/g,
5900
+ `from "./calabasas/_generated/schema"`
5901
+ ],
5902
+ [
5903
+ /from\s+["']\.\/calabasas\/sync["']/g,
5904
+ `from "./calabasas/_generated/sync"`
5905
+ ]
5906
+ ];
5907
+ var GENERATED_IMPORT_REWRITES = {
5908
+ "convex/calabasas/_generated/discord.ts": [
5909
+ [
5910
+ /from\s+["']\.\/(_generated\/server)["']/g,
5911
+ `from "../../_generated/server"`
5912
+ ],
5913
+ [
5914
+ /from\s+["']\.\/(_generated\/api)["']/g,
5915
+ `from "../../_generated/api"`
5916
+ ]
5917
+ ],
5918
+ "convex/calabasas/_generated/sync.ts": [
5919
+ [
5920
+ /from\s+["']\.\.\/(_generated\/server)["']/g,
5921
+ `from "../../_generated/server"`
5922
+ ],
5923
+ [
5924
+ /from\s+["']\.\.\/(_generated\/api)["']/g,
5925
+ `from "../../_generated/api"`
5926
+ ]
5927
+ ]
5928
+ };
5929
+ function isUserFile(filePath) {
5930
+ const rel = path7.relative(path7.resolve(process.cwd(), "convex"), filePath);
5931
+ if (rel.startsWith("_generated"))
5932
+ return false;
5933
+ if (rel.includes("_generated"))
5934
+ return false;
5935
+ return /\.tsx?$/.test(filePath);
5936
+ }
5937
+ var dedicatedFolder = {
5938
+ name: "dedicated-folder",
5939
+ description: "Move all Calabasas files under convex/calabasas/ with _generated/ subdirectory",
5940
+ async run() {
5941
+ const cwd = process.cwd();
5942
+ const convexDir = path7.resolve(cwd, "convex");
5943
+ if (!fs7.existsSync(convexDir)) {
5944
+ p8.log.error("convex/ directory not found.");
5945
+ return;
5946
+ }
5947
+ const generatedDir = path7.join(convexDir, "calabasas", "_generated");
5948
+ if (!fs7.existsSync(generatedDir)) {
5949
+ fs7.mkdirSync(generatedDir, { recursive: true });
5950
+ }
5951
+ let movedCount = 0;
5952
+ for (const [oldRel, newRel] of FILE_MOVES) {
5953
+ const oldPath = path7.join(cwd, oldRel);
5954
+ const newPath = path7.join(cwd, newRel);
5955
+ if (!fs7.existsSync(oldPath))
5956
+ continue;
5957
+ if (fs7.existsSync(newPath)) {
5958
+ p8.log.warn(`Skipped ${oldRel} → ${newRel} (target already exists)`);
5959
+ continue;
5960
+ }
5961
+ fs7.renameSync(oldPath, newPath);
5962
+ p8.log.success(`Moved ${oldRel} → ${newRel}`);
5963
+ movedCount++;
5964
+ }
5965
+ let fixedGeneratedCount = 0;
5966
+ for (const [relPath, rewrites] of Object.entries(GENERATED_IMPORT_REWRITES)) {
5967
+ const absPath = path7.join(cwd, relPath);
5968
+ if (!fs7.existsSync(absPath))
5969
+ continue;
5970
+ let content = fs7.readFileSync(absPath, "utf-8");
5971
+ let changed = false;
5972
+ for (const [pattern, replacement] of rewrites) {
5973
+ const updated = content.replace(pattern, replacement);
5974
+ if (updated !== content) {
5975
+ content = updated;
5976
+ changed = true;
5977
+ }
5978
+ }
5979
+ if (changed) {
5980
+ fs7.writeFileSync(absPath, content);
5981
+ fixedGeneratedCount++;
5982
+ p8.log.success(`Fixed imports in ${relPath}`);
5983
+ }
5984
+ }
5985
+ let rewrittenCount = 0;
5986
+ const userFiles = collectTsFiles(convexDir).filter(isUserFile);
5987
+ for (const filePath of userFiles) {
5988
+ let content = fs7.readFileSync(filePath, "utf-8");
5989
+ let changed = false;
5990
+ for (const [pattern, replacement] of IMPORT_REWRITES) {
5991
+ const updated = content.replace(pattern, replacement);
5992
+ if (updated !== content) {
5993
+ content = updated;
5994
+ changed = true;
5995
+ }
5996
+ }
5997
+ if (changed) {
5998
+ fs7.writeFileSync(filePath, content);
5999
+ const rel = path7.relative(cwd, filePath);
6000
+ p8.log.success(`Rewrote imports in ${rel}`);
6001
+ rewrittenCount++;
6002
+ }
6003
+ }
6004
+ if (movedCount === 0 && rewrittenCount === 0 && fixedGeneratedCount === 0) {
6005
+ p8.log.info("Already up to date.");
6006
+ return;
6007
+ }
6008
+ p8.log.info(`
6009
+ Moved ${movedCount} file${movedCount !== 1 ? "s" : ""}, ` + `fixed imports in ${fixedGeneratedCount + rewrittenCount} file${fixedGeneratedCount + rewrittenCount !== 1 ? "s" : ""}.`);
6010
+ }
6011
+ };
6012
+ function collectTsFiles(dir) {
6013
+ const results = [];
6014
+ const entries = fs7.readdirSync(dir, { withFileTypes: true });
6015
+ for (const entry of entries) {
6016
+ const fullPath = path7.join(dir, entry.name);
6017
+ if (entry.isDirectory()) {
6018
+ results.push(...collectTsFiles(fullPath));
6019
+ } else if (/\.tsx?$/.test(entry.name)) {
6020
+ results.push(fullPath);
6021
+ }
6022
+ }
6023
+ return results;
6024
+ }
6025
+
6026
+ // src/migrations/index.ts
6027
+ var migrations = [dedicatedFolder];
6028
+
6029
+ // src/commands/migrate.ts
6030
+ async function migrate(name) {
6031
+ if (!name) {
6032
+ p9.intro("calabasas migrate");
6033
+ if (migrations.length === 0) {
6034
+ p9.log.info("No migrations available.");
6035
+ p9.outro("Done");
6036
+ return;
6037
+ }
6038
+ p9.log.info(`Available migrations:
6039
+ `);
6040
+ for (const m of migrations) {
6041
+ p9.log.message(` ${m.name} — ${m.description}`);
6042
+ }
6043
+ p9.log.info(`
6044
+ Run a migration with: calabasas migrate <name>`);
6045
+ p9.outro("Done");
6046
+ return;
6047
+ }
6048
+ const migration = migrations.find((m) => m.name === name);
6049
+ if (!migration) {
6050
+ p9.log.error(`Unknown migration: ${name}`);
6051
+ p9.log.info(`Available migrations: ${migrations.map((m) => m.name).join(", ")}`);
6052
+ return;
6053
+ }
6054
+ p9.intro(`calabasas migrate ${name}`);
6055
+ await migration.run();
6056
+ p9.outro("Migration complete!");
6057
+ }
6058
+
6059
+ // src/commands/bot.ts
6060
+ import * as p10 from "@clack/prompts";
5863
6061
  import pc from "picocolors";
5864
6062
  var INTENTS = [
5865
6063
  { name: "Guilds", value: 1 << 0, description: "Guild create/update/delete, roles, channels" },
@@ -5882,9 +6080,9 @@ var INTENTS = [
5882
6080
  { name: "Auto Moderation Config", value: 1 << 20, description: "Auto mod rule changes" },
5883
6081
  { name: "Auto Moderation Execution", value: 1 << 21, description: "Auto mod action execution" }
5884
6082
  ];
5885
- async function apiRequest(method, path7, body, env) {
6083
+ async function apiRequest(method, path8, body, env) {
5886
6084
  const config = getConfig(env);
5887
- const url = `${getApiUrlForEnv(env)}${path7}`;
6085
+ const url = `${getApiUrlForEnv(env)}${path8}`;
5888
6086
  const response = await fetch(url, {
5889
6087
  method,
5890
6088
  headers: {
@@ -5910,43 +6108,43 @@ async function botAdd(options) {
5910
6108
  console.log("Not logged in. Run `calabasas login` first.");
5911
6109
  return;
5912
6110
  }
5913
- p8.intro("calabasas bot add");
5914
- const name = await p8.text({
6111
+ p10.intro("calabasas bot add");
6112
+ const name = await p10.text({
5915
6113
  message: "Bot name",
5916
6114
  placeholder: "My Discord Bot",
5917
6115
  validate: (v) => v.trim() ? undefined : "Bot name is required"
5918
6116
  });
5919
- if (p8.isCancel(name)) {
5920
- p8.cancel("Cancelled.");
6117
+ if (p10.isCancel(name)) {
6118
+ p10.cancel("Cancelled.");
5921
6119
  return;
5922
6120
  }
5923
- const discordAppId = await p8.text({
6121
+ const discordAppId = await p10.text({
5924
6122
  message: "Discord Application ID",
5925
6123
  placeholder: "123456789012345678",
5926
6124
  validate: (v) => v.trim() ? undefined : "Discord Application ID is required"
5927
6125
  });
5928
- if (p8.isCancel(discordAppId)) {
5929
- p8.cancel("Cancelled.");
6126
+ if (p10.isCancel(discordAppId)) {
6127
+ p10.cancel("Cancelled.");
5930
6128
  return;
5931
6129
  }
5932
- const token = await p8.password({
6130
+ const token = await p10.password({
5933
6131
  message: "Bot token",
5934
6132
  validate: (v) => v.trim() ? undefined : "Bot token is required"
5935
6133
  });
5936
- if (p8.isCancel(token)) {
5937
- p8.cancel("Cancelled.");
6134
+ if (p10.isCancel(token)) {
6135
+ p10.cancel("Cancelled.");
5938
6136
  return;
5939
6137
  }
5940
- const convexUrl = await p8.text({
6138
+ const convexUrl = await p10.text({
5941
6139
  message: "Your Convex URL",
5942
6140
  placeholder: "https://your-project.convex.cloud",
5943
6141
  validate: (v) => v.trim() ? undefined : "Convex URL is required"
5944
6142
  });
5945
- if (p8.isCancel(convexUrl)) {
5946
- p8.cancel("Cancelled.");
6143
+ if (p10.isCancel(convexUrl)) {
6144
+ p10.cancel("Cancelled.");
5947
6145
  return;
5948
6146
  }
5949
- const selectedIntents = await p8.multiselect({
6147
+ const selectedIntents = await p10.multiselect({
5950
6148
  message: "Select Gateway Intents",
5951
6149
  options: INTENTS.map((intent, i) => ({
5952
6150
  value: i,
@@ -5956,12 +6154,12 @@ async function botAdd(options) {
5956
6154
  initialValues: [0, 9],
5957
6155
  required: true
5958
6156
  });
5959
- if (p8.isCancel(selectedIntents)) {
5960
- p8.cancel("Cancelled.");
6157
+ if (p10.isCancel(selectedIntents)) {
6158
+ p10.cancel("Cancelled.");
5961
6159
  return;
5962
6160
  }
5963
6161
  const intents = selectedIntents.reduce((sum, i) => sum + INTENTS[i].value, 0);
5964
- const s = p8.spinner();
6162
+ const s = p10.spinner();
5965
6163
  s.start("Creating bot...");
5966
6164
  const { ok, data, status } = await apiRequest("POST", "/api/cli/bots", {
5967
6165
  name: name.trim(),
@@ -5972,16 +6170,16 @@ async function botAdd(options) {
5972
6170
  }, env);
5973
6171
  if (!ok) {
5974
6172
  s.stop("Failed to create bot");
5975
- p8.cancel(`${data.error || `HTTP ${status}`}`);
6173
+ p10.cancel(`${data.error || `HTTP ${status}`}`);
5976
6174
  return;
5977
6175
  }
5978
6176
  s.stop("Bot created!");
5979
6177
  const sharedSecret = data.sharedSecret;
5980
- p8.note(`${sharedSecret}
6178
+ p10.note(`${sharedSecret}
5981
6179
 
5982
6180
  Add this to your Convex environment as CALABASAS_SECRET`, "Shared Secret");
5983
- p8.note("1. Run `calabasas generate` to generate the event handler\n2. Create your handler in convex/discord.ts\n3. Add CALABASAS_SECRET to your Convex environment variables", "Next steps");
5984
- p8.outro("Bot created successfully!");
6181
+ p10.note("1. Run `calabasas generate` to generate the event handler\n2. Create your handler in convex/discord.ts\n3. Add CALABASAS_SECRET to your Convex environment variables", "Next steps");
6182
+ p10.outro("Bot created successfully!");
5985
6183
  }
5986
6184
  async function botList(options) {
5987
6185
  if (options.dev && options.prod) {
@@ -6052,24 +6250,24 @@ async function botRemove(botId, options) {
6052
6250
  console.log("Not logged in. Run `calabasas login` first.");
6053
6251
  return;
6054
6252
  }
6055
- p8.intro("calabasas bot remove");
6056
- const confirmed = await p8.confirm({
6253
+ p10.intro("calabasas bot remove");
6254
+ const confirmed = await p10.confirm({
6057
6255
  message: `Are you sure you want to remove bot ${botId}?`
6058
6256
  });
6059
- if (p8.isCancel(confirmed) || !confirmed) {
6060
- p8.cancel("Cancelled.");
6257
+ if (p10.isCancel(confirmed) || !confirmed) {
6258
+ p10.cancel("Cancelled.");
6061
6259
  return;
6062
6260
  }
6063
- const s = p8.spinner();
6261
+ const s = p10.spinner();
6064
6262
  s.start("Removing bot...");
6065
6263
  const { ok, data, status } = await apiRequest("DELETE", `/api/cli/bots?id=${botId}`, undefined, env);
6066
6264
  if (!ok) {
6067
6265
  s.stop("Failed to remove bot");
6068
- p8.cancel(`${data.error || `HTTP ${status}`}`);
6266
+ p10.cancel(`${data.error || `HTTP ${status}`}`);
6069
6267
  return;
6070
6268
  }
6071
6269
  s.stop("Done");
6072
- p8.outro("Bot removed successfully!");
6270
+ p10.outro("Bot removed successfully!");
6073
6271
  }
6074
6272
  async function botEdit(botId, options) {
6075
6273
  if (options.dev && options.prod) {
@@ -6088,8 +6286,8 @@ async function botEdit(botId, options) {
6088
6286
  const updates = { botId };
6089
6287
  const hasFlags = options.name || options.url || options.token || options.intents;
6090
6288
  if (!hasFlags) {
6091
- p8.intro("calabasas bot edit");
6092
- const field = await p8.select({
6289
+ p10.intro("calabasas bot edit");
6290
+ const field = await p10.select({
6093
6291
  message: "Which field do you want to edit?",
6094
6292
  options: [
6095
6293
  { value: "name", label: "Bot name" },
@@ -6098,35 +6296,35 @@ async function botEdit(botId, options) {
6098
6296
  { value: "intents", label: "Intents value" }
6099
6297
  ]
6100
6298
  });
6101
- if (p8.isCancel(field)) {
6102
- p8.cancel("Cancelled.");
6299
+ if (p10.isCancel(field)) {
6300
+ p10.cancel("Cancelled.");
6103
6301
  return;
6104
6302
  }
6105
6303
  if (field === "name") {
6106
- const value = await p8.text({ message: "New bot name" });
6107
- if (p8.isCancel(value)) {
6108
- p8.cancel("Cancelled.");
6304
+ const value = await p10.text({ message: "New bot name" });
6305
+ if (p10.isCancel(value)) {
6306
+ p10.cancel("Cancelled.");
6109
6307
  return;
6110
6308
  }
6111
6309
  updates.name = value;
6112
6310
  } else if (field === "url") {
6113
- const value = await p8.text({ message: "New Convex URL" });
6114
- if (p8.isCancel(value)) {
6115
- p8.cancel("Cancelled.");
6311
+ const value = await p10.text({ message: "New Convex URL" });
6312
+ if (p10.isCancel(value)) {
6313
+ p10.cancel("Cancelled.");
6116
6314
  return;
6117
6315
  }
6118
6316
  updates.convexUrl = value;
6119
6317
  } else if (field === "token") {
6120
- const value = await p8.password({ message: "New bot token" });
6121
- if (p8.isCancel(value)) {
6122
- p8.cancel("Cancelled.");
6318
+ const value = await p10.password({ message: "New bot token" });
6319
+ if (p10.isCancel(value)) {
6320
+ p10.cancel("Cancelled.");
6123
6321
  return;
6124
6322
  }
6125
6323
  updates.token = value;
6126
6324
  } else if (field === "intents") {
6127
- const value = await p8.text({ message: "New intents value (number)" });
6128
- if (p8.isCancel(value)) {
6129
- p8.cancel("Cancelled.");
6325
+ const value = await p10.text({ message: "New intents value (number)" });
6326
+ if (p10.isCancel(value)) {
6327
+ p10.cancel("Cancelled.");
6130
6328
  return;
6131
6329
  }
6132
6330
  updates.intents = parseInt(value, 10);
@@ -6146,17 +6344,17 @@ async function botEdit(botId, options) {
6146
6344
  console.log("No updates specified.");
6147
6345
  return;
6148
6346
  }
6149
- const s = p8.spinner();
6347
+ const s = p10.spinner();
6150
6348
  s.start("Updating bot...");
6151
6349
  const { ok, data, status } = await apiRequest("PATCH", "/api/cli/bots", updates, env);
6152
6350
  if (!ok) {
6153
6351
  s.stop("Failed to update bot");
6154
- p8.cancel(`${data.error || `HTTP ${status}`}`);
6352
+ p10.cancel(`${data.error || `HTTP ${status}`}`);
6155
6353
  return;
6156
6354
  }
6157
6355
  s.stop("Done");
6158
- p8.note("The Gateway will automatically reconnect with the new settings.", "Note");
6159
- p8.outro("Bot updated successfully!");
6356
+ p10.note("The Gateway will automatically reconnect with the new settings.", "Note");
6357
+ p10.outro("Bot updated successfully!");
6160
6358
  }
6161
6359
 
6162
6360
  // src/index.ts
@@ -6173,10 +6371,11 @@ program2.name("calabasas").description("CLI for Calabasas - Discord Gateway as a
6173
6371
  program2.command("login").description("Authenticate with Calabasas using your API key").option("--dev", "Use development environment").option("--prod", "Use production environment").action(login);
6174
6372
  program2.command("logout").description("Clear stored credentials").option("--dev", "Use development environment").option("--prod", "Use production environment").action(logout);
6175
6373
  program2.command("init").description("Initialize Calabasas config in your Convex project").action(init);
6176
- program2.command("push").description("Push your calabasas.config.ts to Calabasas").option("-c, --config <path>", "Path to config file", "convex/calabasas.config.ts").option("-b, --bot <botId>", "Bot ID to configure (prompts if not specified)").option("--dev", "Push to development environment").option("--prod", "Push to production environment").action(push);
6177
- program2.command("generate").description("Generate discord.generated.ts with type-safe handlers").option("-o, --output <path>", "Output path", "convex/discord.generated.ts").action(generate);
6374
+ program2.command("push").description("Push your Calabasas config to the server").option("-c, --config <path>", "Path to config file", "convex/calabasas/config.ts").option("-b, --bot <botId>", "Bot ID to configure (prompts if not specified)").option("--dev", "Push to development environment").option("--prod", "Push to production environment").action(push);
6375
+ program2.command("generate").description("Generate type-safe Discord handlers and helpers").option("-o, --output <path>", "Output path", "convex/calabasas/_generated/discord.ts").action(generate);
6178
6376
  program2.command("skill").description("Generate Calabasas documentation for AI assistants").option("--dev", "Use development environment").option("--prod", "Use production environment").action(skill);
6179
6377
  program2.command("add [components...]").description("Add Discord UI components to your project").action(add);
6378
+ program2.command("migrate [name]").description("Run a codemod migration (list available if no name given)").action(migrate);
6180
6379
  program2.command("status").alias("dashboard").description("Real-time dashboard showing bot status, events, and stats").option("--dev", "Use development environment").option("--prod", "Use production environment").action(dashboard);
6181
6380
  program2.command("logs [botId]").description("Live event log viewer for a bot").option("-n, --limit <number>", "Number of log entries to show", "50").option("--dev", "Use development environment").option("--prod", "Use production environment").action(logs);
6182
6381
  var bot = program2.command("bot").description("Manage Discord bots");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "calabasas",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "CLI for Calabasas - Discord Gateway as a Service for Convex",
5
5
  "type": "module",
6
6
  "bin": {