calabasas 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/config.d.ts CHANGED
@@ -9,9 +9,21 @@ export type SyncConfig = {
9
9
  presence?: boolean;
10
10
  };
11
11
 
12
- export type EventConfig = {
13
- [eventType: string]: true;
14
- };
12
+ export type DiscordEventName =
13
+ | "messageCreate"
14
+ | "messageUpdate"
15
+ | "messageDelete"
16
+ | "guildMemberAdd"
17
+ | "guildMemberRemove"
18
+ | "guildMemberUpdate"
19
+ | "interactionCreate"
20
+ | "voiceStateUpdate"
21
+ | "presenceUpdate"
22
+ | "typingStart"
23
+ | "messageReactionAdd"
24
+ | "messageReactionRemove";
25
+
26
+ export type EventConfig = { [K in DiscordEventName]?: true };
15
27
 
16
28
  export type CommandOptionType = "string" | "integer" | "number" | "boolean" | "user" | "channel" | "role" | "mentionable" | "attachment";
17
29
 
package/dist/index.js CHANGED
@@ -5425,6 +5425,358 @@ async function botEdit(botId, options) {
5425
5425
  p10.outro("Bot updated successfully!");
5426
5426
  }
5427
5427
 
5428
+ // src/commands/config.ts
5429
+ import * as fs8 from "fs";
5430
+ import * as path8 from "path";
5431
+ import * as p11 from "@clack/prompts";
5432
+ import pc2 from "picocolors";
5433
+
5434
+ // src/lib/configSerializer.ts
5435
+ function indent(str, level) {
5436
+ return str.split(`
5437
+ `).map((line) => line.trim() === "" ? "" : " ".repeat(level) + line).join(`
5438
+ `);
5439
+ }
5440
+ function serializeValue(value, depth) {
5441
+ if (value === null || value === undefined)
5442
+ return "null";
5443
+ if (typeof value === "boolean")
5444
+ return String(value);
5445
+ if (typeof value === "number")
5446
+ return String(value);
5447
+ if (typeof value === "string")
5448
+ return JSON.stringify(value);
5449
+ if (Array.isArray(value)) {
5450
+ if (value.length === 0)
5451
+ return "[]";
5452
+ const allPrimitive = value.every((v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean");
5453
+ if (allPrimitive && value.length <= 5) {
5454
+ return `[${value.map((v) => serializeValue(v, depth + 1)).join(", ")}]`;
5455
+ }
5456
+ const items = value.map((v) => indent(serializeValue(v, depth + 1), depth + 1));
5457
+ return `[
5458
+ ${items.join(`,
5459
+ `)}
5460
+ ${" ".repeat(depth)}]`;
5461
+ }
5462
+ if (typeof value === "object") {
5463
+ const entries = Object.entries(value);
5464
+ if (entries.length === 0)
5465
+ return "{}";
5466
+ const lines = entries.map(([key, val]) => {
5467
+ const safeKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
5468
+ return `${" ".repeat(depth + 1)}${safeKey}: ${serializeValue(val, depth + 1)},`;
5469
+ });
5470
+ return `{
5471
+ ${lines.join(`
5472
+ `)}
5473
+ ${" ".repeat(depth)}}`;
5474
+ }
5475
+ return String(value);
5476
+ }
5477
+ function serializeConfig(config) {
5478
+ const sections = [];
5479
+ if (config.intents && config.intents.length > 0) {
5480
+ const items = config.intents.map((name) => JSON.stringify(name)).join(", ");
5481
+ sections.push(` intents: [${items}],`);
5482
+ }
5483
+ if (config.sync) {
5484
+ const syncLines = Object.entries(config.sync).map(([key, val]) => ` ${key}: ${val},`);
5485
+ sections.push(` sync: {
5486
+ ${syncLines.join(`
5487
+ `)}
5488
+ },`);
5489
+ }
5490
+ if (config.events) {
5491
+ const eventKeys = Object.keys(config.events);
5492
+ if (eventKeys.length > 0) {
5493
+ const eventLines = eventKeys.map((key) => ` ${key}: true,`);
5494
+ sections.push(` events: {
5495
+ ${eventLines.join(`
5496
+ `)}
5497
+ },`);
5498
+ }
5499
+ }
5500
+ if (config.commands) {
5501
+ const commandKeys = Object.keys(config.commands);
5502
+ if (commandKeys.length > 0) {
5503
+ const commandLines = commandKeys.map((name) => {
5504
+ const def = config.commands[name];
5505
+ const serialized = serializeValue(def, 2);
5506
+ return ` ${name}: ${serialized},`;
5507
+ });
5508
+ sections.push(` commands: {
5509
+ ${commandLines.join(`
5510
+ `)}
5511
+ },`);
5512
+ }
5513
+ }
5514
+ return [
5515
+ `import { defineCalabasas } from "calabasas";`,
5516
+ ``,
5517
+ `export default defineCalabasas({`,
5518
+ sections.join(`
5519
+ `),
5520
+ `});`,
5521
+ ``
5522
+ ].join(`
5523
+ `);
5524
+ }
5525
+
5526
+ // src/lib/events.ts
5527
+ var DISCORD_EVENTS_META = {
5528
+ messageCreate: { intent: "GuildMessages", privileged: false },
5529
+ messageUpdate: { intent: "GuildMessages", privileged: false },
5530
+ messageDelete: { intent: "GuildMessages", privileged: false },
5531
+ guildMemberAdd: { intent: "GuildMembers", privileged: true },
5532
+ guildMemberRemove: { intent: "GuildMembers", privileged: true },
5533
+ guildMemberUpdate: { intent: "GuildMembers", privileged: true },
5534
+ interactionCreate: { intent: null, privileged: false },
5535
+ voiceStateUpdate: { intent: "GuildVoiceStates", privileged: false },
5536
+ presenceUpdate: { intent: "GuildPresences", privileged: true },
5537
+ typingStart: { intent: "GuildMessageTyping", privileged: false },
5538
+ messageReactionAdd: { intent: "GuildMessageReactions", privileged: false },
5539
+ messageReactionRemove: { intent: "GuildMessageReactions", privileged: false }
5540
+ };
5541
+
5542
+ // src/lib/sync.ts
5543
+ var SYNC_PROPERTIES_META = {
5544
+ guilds: { intent: null, privileged: false, description: "Sync guild/server info" },
5545
+ channels: { intent: null, privileged: false, description: "Sync channels" },
5546
+ roles: { intent: null, privileged: false, description: "Sync roles" },
5547
+ members: { intent: "GuildMembers", privileged: true, description: "Sync members" },
5548
+ presence: { intent: "GuildPresences", privileged: true, description: "Sync online status" }
5549
+ };
5550
+
5551
+ // src/commands/config.ts
5552
+ var ALL_EVENTS = Object.keys(DISCORD_EVENTS_META);
5553
+ var ALL_SYNC = Object.keys(SYNC_PROPERTIES_META);
5554
+ function resolveConfigPath(configOption) {
5555
+ return path8.resolve(process.cwd(), configOption);
5556
+ }
5557
+ function validateEventNames(names) {
5558
+ const valid = [];
5559
+ const invalid = [];
5560
+ for (const name of names) {
5561
+ if (ALL_EVENTS.includes(name)) {
5562
+ valid.push(name);
5563
+ } else {
5564
+ invalid.push(name);
5565
+ }
5566
+ }
5567
+ return { valid, invalid };
5568
+ }
5569
+ function validateSyncNames(names) {
5570
+ const valid = [];
5571
+ const invalid = [];
5572
+ for (const name of names) {
5573
+ if (ALL_SYNC.includes(name)) {
5574
+ valid.push(name);
5575
+ } else {
5576
+ invalid.push(name);
5577
+ }
5578
+ }
5579
+ return { valid, invalid };
5580
+ }
5581
+ function intentHint(intent, privileged) {
5582
+ if (!intent)
5583
+ return "";
5584
+ const tag = privileged ? pc2.yellow(`requires ${intent} privileged intent`) : pc2.dim(`requires ${intent} intent`);
5585
+ return ` ${tag}`;
5586
+ }
5587
+ function listConfig(config, configPath) {
5588
+ const relPath = path8.relative(process.cwd(), configPath);
5589
+ console.log(`
5590
+ ${pc2.bold("Calabasas Config")} ${pc2.dim(`(${relPath})`)}
5591
+ `);
5592
+ console.log(pc2.bold("Sync:"));
5593
+ for (const key of ALL_SYNC) {
5594
+ const meta = SYNC_PROPERTIES_META[key];
5595
+ const enabled = config.sync?.[key] === true;
5596
+ const dot = enabled ? pc2.green(" ●") : pc2.dim(" ○");
5597
+ const hint = !enabled ? intentHint(meta.intent, meta.privileged) : "";
5598
+ console.log(`${dot} ${key}${hint}`);
5599
+ }
5600
+ console.log(`
5601
+ ${pc2.bold("Events:")}`);
5602
+ const enabledEvents = new Set(Object.keys(config.events ?? {}));
5603
+ for (const key of ALL_EVENTS) {
5604
+ const meta = DISCORD_EVENTS_META[key];
5605
+ const enabled = enabledEvents.has(key);
5606
+ const dot = enabled ? pc2.green(" ●") : pc2.dim(" ○");
5607
+ const hint = !enabled ? intentHint(meta.intent, meta.privileged) : "";
5608
+ console.log(`${dot} ${key}${hint}`);
5609
+ }
5610
+ const commandNames = Object.keys(config.commands ?? {});
5611
+ if (commandNames.length > 0) {
5612
+ console.log(`
5613
+ ${pc2.bold("Commands:")}`);
5614
+ for (const name of commandNames) {
5615
+ console.log(` /${name} — ${config.commands[name].description}`);
5616
+ }
5617
+ }
5618
+ console.log("");
5619
+ }
5620
+ async function interactiveConfig(config, configPath) {
5621
+ p11.intro("calabasas config");
5622
+ const syncSelection = await p11.multiselect({
5623
+ message: "Select sync properties to enable",
5624
+ options: ALL_SYNC.map((key) => {
5625
+ const meta = SYNC_PROPERTIES_META[key];
5626
+ const hint = meta.privileged ? `${meta.description} (Privileged)` : meta.description;
5627
+ return {
5628
+ value: key,
5629
+ label: meta.privileged ? pc2.yellow(key) : key,
5630
+ hint
5631
+ };
5632
+ }),
5633
+ initialValues: ALL_SYNC.filter((key) => config.sync?.[key] === true),
5634
+ required: false
5635
+ });
5636
+ if (p11.isCancel(syncSelection)) {
5637
+ p11.cancel("Cancelled.");
5638
+ return;
5639
+ }
5640
+ const eventSelection = await p11.multiselect({
5641
+ message: "Select events to enable",
5642
+ options: ALL_EVENTS.map((key) => {
5643
+ const meta = DISCORD_EVENTS_META[key];
5644
+ const hint = meta.intent ? meta.privileged ? `${meta.intent} (Privileged)` : meta.intent : "No intent required";
5645
+ return {
5646
+ value: key,
5647
+ label: meta.privileged ? pc2.yellow(key) : key,
5648
+ hint
5649
+ };
5650
+ }),
5651
+ initialValues: Object.keys(config.events ?? {}),
5652
+ required: false
5653
+ });
5654
+ if (p11.isCancel(eventSelection)) {
5655
+ p11.cancel("Cancelled.");
5656
+ return;
5657
+ }
5658
+ const selectedSync = new Set(syncSelection);
5659
+ const selectedEvents = new Set(eventSelection);
5660
+ const newConfig = { ...config };
5661
+ const newSync = {};
5662
+ for (const key of ALL_SYNC) {
5663
+ newSync[key] = selectedSync.has(key);
5664
+ }
5665
+ newConfig.sync = newSync;
5666
+ const newEvents = {};
5667
+ for (const key of selectedEvents) {
5668
+ newEvents[key] = true;
5669
+ }
5670
+ newConfig.events = newEvents;
5671
+ const oldSync = config.sync ?? {};
5672
+ const oldEvents = new Set(Object.keys(config.events ?? {}));
5673
+ const syncChanged = ALL_SYNC.some((key) => oldSync[key] === true !== selectedSync.has(key));
5674
+ const eventsChanged = selectedEvents.size !== oldEvents.size || [...selectedEvents].some((e) => !oldEvents.has(e));
5675
+ if (!syncChanged && !eventsChanged) {
5676
+ p11.outro("No changes.");
5677
+ return;
5678
+ }
5679
+ const changes = [];
5680
+ for (const key of ALL_SYNC) {
5681
+ const was = oldSync[key] === true;
5682
+ const now = selectedSync.has(key);
5683
+ if (was && !now)
5684
+ changes.push(`sync.${key}: ${pc2.red("enabled → disabled")}`);
5685
+ if (!was && now)
5686
+ changes.push(`sync.${key}: ${pc2.green("disabled → enabled")}`);
5687
+ }
5688
+ for (const key of ALL_EVENTS) {
5689
+ const was = oldEvents.has(key);
5690
+ const now = selectedEvents.has(key);
5691
+ if (was && !now)
5692
+ changes.push(`events.${key}: ${pc2.red("enabled → disabled")}`);
5693
+ if (!was && now)
5694
+ changes.push(`events.${key}: ${pc2.green("disabled → enabled")}`);
5695
+ }
5696
+ p11.note(changes.join(`
5697
+ `), "Changes");
5698
+ const source = serializeConfig(newConfig);
5699
+ fs8.writeFileSync(configPath, source);
5700
+ p11.log.info(pc2.dim("Note: Comments in the original file are not preserved."));
5701
+ p11.outro(`Updated ${path8.relative(process.cwd(), configPath)}. Run ${pc2.cyan("`calabasas generate`")} to regenerate code.`);
5702
+ }
5703
+ async function config(options) {
5704
+ const configPath = resolveConfigPath(options.config);
5705
+ if (!fs8.existsSync(configPath)) {
5706
+ console.error(`Error: Config file not found: ${configPath}`);
5707
+ console.error(`
5708
+ Run ${pc2.cyan("`calabasas init`")} first.`);
5709
+ process.exit(1);
5710
+ }
5711
+ const currentConfig = await parseConfigFile(configPath);
5712
+ if (options.list) {
5713
+ listConfig(currentConfig, configPath);
5714
+ return;
5715
+ }
5716
+ const hasFlags = options.addEvent?.length || options.removeEvent?.length || options.addSync?.length || options.removeSync?.length;
5717
+ if (hasFlags) {
5718
+ if (options.addEvent?.length) {
5719
+ const { invalid } = validateEventNames(options.addEvent);
5720
+ if (invalid.length > 0) {
5721
+ console.error(`Error: Unknown event names: ${invalid.join(", ")}`);
5722
+ console.error(`Valid events: ${ALL_EVENTS.join(", ")}`);
5723
+ process.exit(1);
5724
+ }
5725
+ }
5726
+ if (options.removeEvent?.length) {
5727
+ const { invalid } = validateEventNames(options.removeEvent);
5728
+ if (invalid.length > 0) {
5729
+ console.error(`Error: Unknown event names: ${invalid.join(", ")}`);
5730
+ console.error(`Valid events: ${ALL_EVENTS.join(", ")}`);
5731
+ process.exit(1);
5732
+ }
5733
+ }
5734
+ if (options.addSync?.length) {
5735
+ const { invalid } = validateSyncNames(options.addSync);
5736
+ if (invalid.length > 0) {
5737
+ console.error(`Error: Unknown sync properties: ${invalid.join(", ")}`);
5738
+ console.error(`Valid properties: ${ALL_SYNC.join(", ")}`);
5739
+ process.exit(1);
5740
+ }
5741
+ }
5742
+ if (options.removeSync?.length) {
5743
+ const { invalid } = validateSyncNames(options.removeSync);
5744
+ if (invalid.length > 0) {
5745
+ console.error(`Error: Unknown sync properties: ${invalid.join(", ")}`);
5746
+ console.error(`Valid properties: ${ALL_SYNC.join(", ")}`);
5747
+ process.exit(1);
5748
+ }
5749
+ }
5750
+ const newConfig = { ...currentConfig };
5751
+ if (options.addSync?.length || options.removeSync?.length) {
5752
+ const sync = { ...currentConfig.sync };
5753
+ for (const name of options.addSync ?? []) {
5754
+ sync[name] = true;
5755
+ }
5756
+ for (const name of options.removeSync ?? []) {
5757
+ sync[name] = false;
5758
+ }
5759
+ newConfig.sync = sync;
5760
+ }
5761
+ if (options.addEvent?.length || options.removeEvent?.length) {
5762
+ const events = { ...currentConfig.events };
5763
+ for (const name of options.addEvent ?? []) {
5764
+ events[name] = true;
5765
+ }
5766
+ for (const name of options.removeEvent ?? []) {
5767
+ delete events[name];
5768
+ }
5769
+ newConfig.events = events;
5770
+ }
5771
+ const source = serializeConfig(newConfig);
5772
+ fs8.writeFileSync(configPath, source);
5773
+ console.log(`Updated ${path8.relative(process.cwd(), configPath)}.`);
5774
+ console.log(`Run ${pc2.cyan("`calabasas generate`")} to regenerate code.`);
5775
+ return;
5776
+ }
5777
+ await interactiveConfig(currentConfig, configPath);
5778
+ }
5779
+
5428
5780
  // src/index.ts
5429
5781
  var dashboard = async (...args) => {
5430
5782
  const mod = await import("./dashboard-r2cptf7m.js");
@@ -5444,6 +5796,7 @@ program2.command("generate").description("Generate type-safe Discord handlers an
5444
5796
  program2.command("skill").description("Generate Calabasas documentation for AI assistants").option("--dev", "Use development environment").option("--prod", "Use production environment").option("-f, --file <path>", "Target file path (e.g. CLAUDE.md)").option("-y, --yes", "Auto-confirm replacing existing guidelines").action(skill);
5445
5797
  program2.command("add [components...]").description("Add Discord UI components to your project").action(add);
5446
5798
  program2.command("migrate [name]").description("Run a codemod migration (list available if no name given)").action(migrate);
5799
+ program2.command("config").description("View and modify your Calabasas config").option("-c, --config <path>", "Path to config file", "convex/calabasas/config.ts").option("-l, --list", "List current and available config options").option("--add-event <events...>", "Enable event handlers").option("--remove-event <events...>", "Disable event handlers").option("--add-sync <properties...>", "Enable sync properties").option("--remove-sync <properties...>", "Disable sync properties").action(config);
5447
5800
  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);
5448
5801
  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);
5449
5802
  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.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "CLI for Calabasas - Discord Gateway as a Service for Convex",
5
5
  "type": "module",
6
6
  "bin": {