calabasas 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +208 -46
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2298,6 +2298,7 @@ async function push(options) {
|
|
|
2298
2298
|
syncChannels: calabasasConfig.sync?.channels ?? false,
|
|
2299
2299
|
syncRoles: calabasasConfig.sync?.roles ?? false,
|
|
2300
2300
|
syncMembers: calabasasConfig.sync?.members ?? false,
|
|
2301
|
+
syncPresence: calabasasConfig.sync?.presence ?? false,
|
|
2301
2302
|
eventConfigs
|
|
2302
2303
|
})
|
|
2303
2304
|
});
|
|
@@ -2333,7 +2334,8 @@ async function push(options) {
|
|
|
2333
2334
|
`Guilds: ${calabasasConfig.sync?.guilds ? "enabled" : "disabled"}`,
|
|
2334
2335
|
`Channels: ${calabasasConfig.sync?.channels ? "enabled" : "disabled"}`,
|
|
2335
2336
|
`Roles: ${calabasasConfig.sync?.roles ? "enabled" : "disabled"}`,
|
|
2336
|
-
`Members: ${calabasasConfig.sync?.members ? "enabled" : "disabled"}
|
|
2337
|
+
`Members: ${calabasasConfig.sync?.members ? "enabled" : "disabled"}`,
|
|
2338
|
+
`Presence: ${calabasasConfig.sync?.presence ? "enabled" : "disabled"}`
|
|
2337
2339
|
].join(`
|
|
2338
2340
|
`);
|
|
2339
2341
|
const eventSummary = eventConfigs.length > 0 ? `
|
|
@@ -2427,6 +2429,16 @@ function generateSchemaFile(sync) {
|
|
|
2427
2429
|
.index("by_user_guild", ["discordUserId", "guildDiscordId"])
|
|
2428
2430
|
.index("by_guild", ["guildDiscordId"]);`);
|
|
2429
2431
|
}
|
|
2432
|
+
if (sync.presence) {
|
|
2433
|
+
tables.push(`export const calabasasPresence = defineTable({
|
|
2434
|
+
discordUserId: v.string(),
|
|
2435
|
+
guildDiscordId: v.string(),
|
|
2436
|
+
status: v.string(),
|
|
2437
|
+
updatedAt: v.number(),
|
|
2438
|
+
})
|
|
2439
|
+
.index("by_guild", ["guildDiscordId"])
|
|
2440
|
+
.index("by_user_guild", ["discordUserId", "guildDiscordId"]);`);
|
|
2441
|
+
}
|
|
2430
2442
|
const tableNames = [];
|
|
2431
2443
|
if (sync.guilds) {
|
|
2432
2444
|
tableNames.push("calabasasGuilds");
|
|
@@ -2438,6 +2450,8 @@ function generateSchemaFile(sync) {
|
|
|
2438
2450
|
tableNames.push("calabasasRoles");
|
|
2439
2451
|
if (sync.members)
|
|
2440
2452
|
tableNames.push("calabasasMembers");
|
|
2453
|
+
if (sync.presence)
|
|
2454
|
+
tableNames.push("calabasasPresence");
|
|
2441
2455
|
const tablesExport = tableNames.length > 0 ? `export const calabasasTables = {
|
|
2442
2456
|
${tableNames.join(`,
|
|
2443
2457
|
`)},
|
|
@@ -2549,6 +2563,16 @@ var MEMBER_TYPE = `{
|
|
|
2549
2563
|
roles?: string[];
|
|
2550
2564
|
joinedAt?: string;
|
|
2551
2565
|
}`;
|
|
2566
|
+
var PRESENCE_VALIDATOR = `v.object({
|
|
2567
|
+
userId: v.string(),
|
|
2568
|
+
guildId: v.string(),
|
|
2569
|
+
status: v.string(),
|
|
2570
|
+
})`;
|
|
2571
|
+
var PRESENCE_TYPE = `{
|
|
2572
|
+
userId: string;
|
|
2573
|
+
guildId: string;
|
|
2574
|
+
status: string;
|
|
2575
|
+
}`;
|
|
2552
2576
|
var GUILD_ADMIN_VALIDATOR = `v.object({
|
|
2553
2577
|
guildId: v.string(),
|
|
2554
2578
|
adminUserIds: v.array(v.string()),
|
|
@@ -2736,6 +2760,42 @@ function generateMemberMutation() {
|
|
|
2736
2760
|
},
|
|
2737
2761
|
});`;
|
|
2738
2762
|
}
|
|
2763
|
+
function generatePresenceMutation() {
|
|
2764
|
+
return `export const syncPresence = internalMutation({
|
|
2765
|
+
args: {
|
|
2766
|
+
data: presenceValidator,
|
|
2767
|
+
operation: v.union(v.literal("upsert"), v.literal("delete")),
|
|
2768
|
+
},
|
|
2769
|
+
returns: v.null(),
|
|
2770
|
+
handler: async (ctx, { data: presence, operation }) => {
|
|
2771
|
+
const existing = await ctx.db
|
|
2772
|
+
.query("calabasasPresence")
|
|
2773
|
+
.withIndex("by_user_guild", (q) =>
|
|
2774
|
+
q.eq("discordUserId", presence.userId).eq("guildDiscordId", presence.guildId)
|
|
2775
|
+
)
|
|
2776
|
+
.unique();
|
|
2777
|
+
|
|
2778
|
+
if (operation === "delete") {
|
|
2779
|
+
if (existing) await ctx.db.delete(existing._id);
|
|
2780
|
+
return null;
|
|
2781
|
+
}
|
|
2782
|
+
|
|
2783
|
+
const doc = {
|
|
2784
|
+
discordUserId: presence.userId,
|
|
2785
|
+
guildDiscordId: presence.guildId,
|
|
2786
|
+
status: presence.status,
|
|
2787
|
+
updatedAt: Date.now(),
|
|
2788
|
+
};
|
|
2789
|
+
|
|
2790
|
+
if (existing) {
|
|
2791
|
+
await ctx.db.patch(existing._id, doc);
|
|
2792
|
+
} else {
|
|
2793
|
+
await ctx.db.insert("calabasasPresence", doc);
|
|
2794
|
+
}
|
|
2795
|
+
return null;
|
|
2796
|
+
},
|
|
2797
|
+
});`;
|
|
2798
|
+
}
|
|
2739
2799
|
function generateGuildAdminMutation() {
|
|
2740
2800
|
return `export const _syncGuildAdmins = internalMutation({
|
|
2741
2801
|
args: {
|
|
@@ -2854,6 +2914,13 @@ function generateSyncFile(sync) {
|
|
|
2854
2914
|
publicMutations.push(generatePublicSyncMutation("member"));
|
|
2855
2915
|
enabledTypes.push("member");
|
|
2856
2916
|
}
|
|
2917
|
+
if (sync.presence) {
|
|
2918
|
+
validators.push(`const presenceValidator = ${PRESENCE_VALIDATOR};`);
|
|
2919
|
+
types.push(`export type PresenceSync = ${PRESENCE_TYPE};`);
|
|
2920
|
+
internalMutations.push(generateInternalSyncMutation("presence", generatePresenceMutation()));
|
|
2921
|
+
publicMutations.push(generatePublicSyncMutation("presence"));
|
|
2922
|
+
enabledTypes.push("presence");
|
|
2923
|
+
}
|
|
2857
2924
|
if (internalMutations.length === 0) {
|
|
2858
2925
|
return `/**
|
|
2859
2926
|
* THIS FILE IS AUTO-GENERATED BY \`calabasas generate\`
|
|
@@ -3499,7 +3566,7 @@ async function generate(options) {
|
|
|
3499
3566
|
fs4.writeFileSync(eventHandlersPath, eventHandlersCode);
|
|
3500
3567
|
const generated = [options.output];
|
|
3501
3568
|
const syncConfig = config.sync ?? {};
|
|
3502
|
-
const hasSyncEnabled = syncConfig.guilds || syncConfig.channels || syncConfig.roles || syncConfig.members;
|
|
3569
|
+
const hasSyncEnabled = syncConfig.guilds || syncConfig.channels || syncConfig.roles || syncConfig.members || syncConfig.presence;
|
|
3503
3570
|
if (hasSyncEnabled) {
|
|
3504
3571
|
const schemaPath = path4.join(generatedDir, "schema.ts");
|
|
3505
3572
|
const schemaCode = generateSchemaFile(syncConfig);
|
|
@@ -3529,6 +3596,8 @@ async function generate(options) {
|
|
|
3529
3596
|
syncExports.push("syncRole");
|
|
3530
3597
|
if (syncConfig.members)
|
|
3531
3598
|
syncExports.push("syncMember");
|
|
3599
|
+
if (syncConfig.presence)
|
|
3600
|
+
syncExports.push("syncPresence");
|
|
3532
3601
|
p5.note(`1. Add tables to your convex/schema.ts:
|
|
3533
3602
|
import { calabasasTables } from "./calabasas/_generated/schema";
|
|
3534
3603
|
|
|
@@ -3609,17 +3678,23 @@ async function skill(options) {
|
|
|
3609
3678
|
const cwd = process.cwd();
|
|
3610
3679
|
const detectedFiles = detectExistingFiles(cwd);
|
|
3611
3680
|
if (detectedFiles.length === 0) {
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
options
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3681
|
+
let createPath;
|
|
3682
|
+
if (options.file) {
|
|
3683
|
+
createPath = options.file;
|
|
3684
|
+
} else {
|
|
3685
|
+
const selected = await p6.select({
|
|
3686
|
+
message: "No CLAUDE.md or AGENTS.md found. Where should we create one?",
|
|
3687
|
+
options: [
|
|
3688
|
+
{ value: "CLAUDE.md", label: "CLAUDE.md (project root)" },
|
|
3689
|
+
{ value: "AGENTS.md", label: "AGENTS.md (project root)" },
|
|
3690
|
+
{ value: ".claude/CLAUDE.md", label: ".claude/CLAUDE.md" }
|
|
3691
|
+
]
|
|
3692
|
+
});
|
|
3693
|
+
if (p6.isCancel(selected)) {
|
|
3694
|
+
p6.cancel("Cancelled.");
|
|
3695
|
+
return;
|
|
3696
|
+
}
|
|
3697
|
+
createPath = selected;
|
|
3623
3698
|
}
|
|
3624
3699
|
const fullPath = path5.resolve(cwd, createPath);
|
|
3625
3700
|
const dir = path5.dirname(fullPath);
|
|
@@ -3635,7 +3710,33 @@ ${skillContent}`;
|
|
|
3635
3710
|
return;
|
|
3636
3711
|
}
|
|
3637
3712
|
let selectedFile;
|
|
3638
|
-
if (
|
|
3713
|
+
if (options.file) {
|
|
3714
|
+
const match = detectedFiles.find((f) => f.path === options.file);
|
|
3715
|
+
if (match) {
|
|
3716
|
+
selectedFile = match;
|
|
3717
|
+
} else {
|
|
3718
|
+
const fullPath = path5.resolve(cwd, options.file);
|
|
3719
|
+
const dir = path5.dirname(fullPath);
|
|
3720
|
+
if (!fs5.existsSync(dir)) {
|
|
3721
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
3722
|
+
}
|
|
3723
|
+
if (fs5.existsSync(fullPath)) {
|
|
3724
|
+
selectedFile = {
|
|
3725
|
+
path: options.file,
|
|
3726
|
+
fullPath,
|
|
3727
|
+
type: options.file.includes("CLAUDE") ? "CLAUDE.md" : "AGENTS.md"
|
|
3728
|
+
};
|
|
3729
|
+
} else {
|
|
3730
|
+
const projectName = path5.basename(cwd);
|
|
3731
|
+
const initialContent = `# ${projectName}
|
|
3732
|
+
|
|
3733
|
+
${skillContent}`;
|
|
3734
|
+
fs5.writeFileSync(fullPath, initialContent);
|
|
3735
|
+
p6.outro(`Created ${options.file} with Calabasas guidelines`);
|
|
3736
|
+
return;
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
} else if (detectedFiles.length === 1) {
|
|
3639
3740
|
selectedFile = detectedFiles[0];
|
|
3640
3741
|
} else {
|
|
3641
3742
|
const selected = await p6.select({
|
|
@@ -3653,12 +3754,14 @@ ${skillContent}`;
|
|
|
3653
3754
|
}
|
|
3654
3755
|
const existingContent = fs5.readFileSync(selectedFile.fullPath, "utf8");
|
|
3655
3756
|
if (hasCalabsasSection(existingContent)) {
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
p6.
|
|
3661
|
-
|
|
3757
|
+
if (!options.yes) {
|
|
3758
|
+
const update = await p6.confirm({
|
|
3759
|
+
message: `Calabasas guidelines already exist in ${selectedFile.path}. Replace?`
|
|
3760
|
+
});
|
|
3761
|
+
if (p6.isCancel(update) || !update) {
|
|
3762
|
+
p6.cancel("Cancelled.");
|
|
3763
|
+
return;
|
|
3764
|
+
}
|
|
3662
3765
|
}
|
|
3663
3766
|
const sectionRegex = /## Calabasas Guidelines[\s\S]*?(?=\n## |\n# |$)/;
|
|
3664
3767
|
const contentWithoutSection = existingContent.replace(sectionRegex, "").trimEnd();
|
|
@@ -3687,6 +3790,7 @@ import * as p7 from "@clack/prompts";
|
|
|
3687
3790
|
// src/lib/registry/components/channel-select.ts
|
|
3688
3791
|
var channelSelect = {
|
|
3689
3792
|
name: "channel-select",
|
|
3793
|
+
kind: "component",
|
|
3690
3794
|
description: "Searchable combobox to pick a Discord channel (with type icons)",
|
|
3691
3795
|
requiredSyncTypes: ["channels"],
|
|
3692
3796
|
requiredShadcnComponents: ["popover", "command", "button"],
|
|
@@ -3834,6 +3938,7 @@ export function ChannelSelect({
|
|
|
3834
3938
|
// src/lib/registry/components/role-select.ts
|
|
3835
3939
|
var roleSelect = {
|
|
3836
3940
|
name: "role-select",
|
|
3941
|
+
kind: "component",
|
|
3837
3942
|
description: "Searchable combobox to pick a Discord role (with color dots)",
|
|
3838
3943
|
requiredSyncTypes: ["roles"],
|
|
3839
3944
|
requiredShadcnComponents: ["popover", "command", "button"],
|
|
@@ -3972,6 +4077,7 @@ export function RoleSelect({
|
|
|
3972
4077
|
// src/lib/registry/components/member-select.ts
|
|
3973
4078
|
var memberSelect = {
|
|
3974
4079
|
name: "member-select",
|
|
4080
|
+
kind: "component",
|
|
3975
4081
|
description: "Searchable combobox to pick a Discord member (with avatar)",
|
|
3976
4082
|
requiredSyncTypes: ["members"],
|
|
3977
4083
|
requiredShadcnComponents: ["popover", "command", "button"],
|
|
@@ -4123,6 +4229,7 @@ export function MemberSelect({
|
|
|
4123
4229
|
// src/lib/registry/components/guild-select.ts
|
|
4124
4230
|
var guildSelect = {
|
|
4125
4231
|
name: "guild-select",
|
|
4232
|
+
kind: "component",
|
|
4126
4233
|
description: "Searchable combobox to pick a Discord server (with icon)",
|
|
4127
4234
|
requiredSyncTypes: ["guilds"],
|
|
4128
4235
|
requiredShadcnComponents: ["popover", "command", "button"],
|
|
@@ -4278,6 +4385,7 @@ export function GuildSelect({
|
|
|
4278
4385
|
// src/lib/registry/components/role-badge.ts
|
|
4279
4386
|
var roleBadge = {
|
|
4280
4387
|
name: "role-badge",
|
|
4388
|
+
kind: "component",
|
|
4281
4389
|
description: "Colored role pill badges that match Discord's role tags",
|
|
4282
4390
|
requiredSyncTypes: ["roles"],
|
|
4283
4391
|
requiredShadcnComponents: ["badge", "tooltip"],
|
|
@@ -4421,6 +4529,7 @@ export function RoleBadge({
|
|
|
4421
4529
|
// src/lib/registry/components/server-info.ts
|
|
4422
4530
|
var serverInfo = {
|
|
4423
4531
|
name: "server-info",
|
|
4532
|
+
kind: "component",
|
|
4424
4533
|
description: "Guild overview card with icon, name, boost tier, and feature tags",
|
|
4425
4534
|
requiredSyncTypes: ["guilds"],
|
|
4426
4535
|
requiredShadcnComponents: ["card", "avatar", "badge"],
|
|
@@ -4623,6 +4732,7 @@ export function ServerInfo({
|
|
|
4623
4732
|
// src/lib/registry/components/permission-viewer.ts
|
|
4624
4733
|
var permissionViewer = {
|
|
4625
4734
|
name: "permission-viewer",
|
|
4735
|
+
kind: "component",
|
|
4626
4736
|
description: "Read-only permission grid that parses Discord permission bitfields",
|
|
4627
4737
|
requiredSyncTypes: ["roles"],
|
|
4628
4738
|
requiredShadcnComponents: ["card", "badge", "tooltip", "separator"],
|
|
@@ -4855,6 +4965,7 @@ export function PermissionViewer({
|
|
|
4855
4965
|
// src/lib/registry/components/member-card.ts
|
|
4856
4966
|
var memberCard = {
|
|
4857
4967
|
name: "member-card",
|
|
4968
|
+
kind: "component",
|
|
4858
4969
|
description: "Profile card with avatar, name, role badges, and join date",
|
|
4859
4970
|
requiredSyncTypes: ["members", "roles"],
|
|
4860
4971
|
requiredShadcnComponents: ["card", "avatar", "badge", "separator"],
|
|
@@ -5091,6 +5202,7 @@ export function MemberCard({
|
|
|
5091
5202
|
// src/lib/registry/components/channel-tree.ts
|
|
5092
5203
|
var channelTree = {
|
|
5093
5204
|
name: "channel-tree",
|
|
5205
|
+
kind: "component",
|
|
5094
5206
|
description: "Hierarchical channel sidebar grouped by category with type icons",
|
|
5095
5207
|
requiredSyncTypes: ["channels"],
|
|
5096
5208
|
requiredShadcnComponents: ["collapsible", "scroll-area", "tooltip"],
|
|
@@ -5358,6 +5470,7 @@ function ChannelItem({
|
|
|
5358
5470
|
// src/lib/registry/components/member-roster.ts
|
|
5359
5471
|
var memberRoster = {
|
|
5360
5472
|
name: "member-roster",
|
|
5473
|
+
kind: "component",
|
|
5361
5474
|
description: "Member list grouped by hoisted roles, like Discord's right sidebar",
|
|
5362
5475
|
requiredSyncTypes: ["members", "roles"],
|
|
5363
5476
|
requiredShadcnComponents: ["scroll-area", "avatar", "badge", "separator"],
|
|
@@ -5614,6 +5727,44 @@ export function MemberRoster({
|
|
|
5614
5727
|
});`
|
|
5615
5728
|
};
|
|
5616
5729
|
|
|
5730
|
+
// src/lib/registry/components/use-online-count.ts
|
|
5731
|
+
var useOnlineCount = {
|
|
5732
|
+
name: "use-online-count",
|
|
5733
|
+
kind: "hook",
|
|
5734
|
+
description: "React hook that returns the number of online members in a guild",
|
|
5735
|
+
requiredSyncTypes: ["presence"],
|
|
5736
|
+
requiredShadcnComponents: [],
|
|
5737
|
+
generateReactComponent: () => `"use client";
|
|
5738
|
+
|
|
5739
|
+
import { useQuery } from "convex/react";
|
|
5740
|
+
import { api } from "@/convex/_generated/api";
|
|
5741
|
+
|
|
5742
|
+
/**
|
|
5743
|
+
* Returns the number of online members in a Discord guild.
|
|
5744
|
+
*
|
|
5745
|
+
* Requires \`sync.presence: true\` in your calabasas config.
|
|
5746
|
+
* The GuildPresences privileged intent must be enabled for your bot.
|
|
5747
|
+
*
|
|
5748
|
+
* @param guildDiscordId - The Discord guild ID
|
|
5749
|
+
* @returns The online member count, or \`undefined\` while loading
|
|
5750
|
+
*/
|
|
5751
|
+
export function useOnlineCount(guildDiscordId: string): number | undefined {
|
|
5752
|
+
return useQuery(api.calabasas.queries.onlineCount, { guildDiscordId });
|
|
5753
|
+
}
|
|
5754
|
+
`,
|
|
5755
|
+
generateConvexQueries: () => `export const onlineCount = query({
|
|
5756
|
+
args: { guildDiscordId: v.string() },
|
|
5757
|
+
returns: v.number(),
|
|
5758
|
+
handler: async (ctx, { guildDiscordId }) => {
|
|
5759
|
+
const presences = await ctx.db
|
|
5760
|
+
.query("calabasasPresence")
|
|
5761
|
+
.withIndex("by_guild", (q) => q.eq("guildDiscordId", guildDiscordId))
|
|
5762
|
+
.collect();
|
|
5763
|
+
return presences.length;
|
|
5764
|
+
},
|
|
5765
|
+
});`
|
|
5766
|
+
};
|
|
5767
|
+
|
|
5617
5768
|
// src/lib/registry/index.ts
|
|
5618
5769
|
var REGISTRY = [
|
|
5619
5770
|
channelSelect,
|
|
@@ -5625,7 +5776,8 @@ var REGISTRY = [
|
|
|
5625
5776
|
permissionViewer,
|
|
5626
5777
|
memberCard,
|
|
5627
5778
|
channelTree,
|
|
5628
|
-
memberRoster
|
|
5779
|
+
memberRoster,
|
|
5780
|
+
useOnlineCount
|
|
5629
5781
|
];
|
|
5630
5782
|
function getComponent(name) {
|
|
5631
5783
|
return REGISTRY.find((c) => c.name === name);
|
|
@@ -5646,6 +5798,8 @@ function parseEnabledSyncTypes(configPath) {
|
|
|
5646
5798
|
enabled.add("roles");
|
|
5647
5799
|
if (/members:\s*true/.test(syncContent))
|
|
5648
5800
|
enabled.add("members");
|
|
5801
|
+
if (/presence:\s*true/.test(syncContent))
|
|
5802
|
+
enabled.add("presence");
|
|
5649
5803
|
}
|
|
5650
5804
|
return enabled;
|
|
5651
5805
|
}
|
|
@@ -5737,9 +5891,10 @@ async function add(componentNames) {
|
|
|
5737
5891
|
if (!fs6.existsSync(componentsDir)) {
|
|
5738
5892
|
fs6.mkdirSync(componentsDir, { recursive: true });
|
|
5739
5893
|
}
|
|
5740
|
-
const
|
|
5894
|
+
const ext = comp.kind === "hook" ? ".ts" : ".tsx";
|
|
5895
|
+
const componentPath = path6.join(componentsDir, `${comp.name}${ext}`);
|
|
5741
5896
|
fs6.writeFileSync(componentPath, comp.generateReactComponent());
|
|
5742
|
-
p7.log.success(`Created components/calabasas/${comp.name}
|
|
5897
|
+
p7.log.success(`Created components/calabasas/${comp.name}${ext}`);
|
|
5743
5898
|
const queryBlock = comp.generateConvexQueries();
|
|
5744
5899
|
const namesInBlock = queryNamesInBlock(queryBlock);
|
|
5745
5900
|
const newNames = namesInBlock.filter((n) => !existing.has(n));
|
|
@@ -5790,45 +5945,52 @@ Install with: npx shadcn@latest add ${missingShadcn.join(" ")}`, "Required shadc
|
|
|
5790
5945
|
}
|
|
5791
5946
|
const firstInstalled = components[0];
|
|
5792
5947
|
if (firstInstalled) {
|
|
5793
|
-
const pascal = toPascalCase(firstInstalled.name);
|
|
5794
|
-
const needsGuild = firstInstalled.requiredSyncTypes.some((t) => t === "channels" || t === "roles" || t === "members");
|
|
5795
|
-
const displayComponents = new Set([
|
|
5796
|
-
"role-badge",
|
|
5797
|
-
"server-info",
|
|
5798
|
-
"permission-viewer",
|
|
5799
|
-
"member-card"
|
|
5800
|
-
]);
|
|
5801
|
-
const isDisplay = displayComponents.has(firstInstalled.name);
|
|
5802
5948
|
let usage;
|
|
5803
|
-
if (
|
|
5804
|
-
const
|
|
5805
|
-
|
|
5949
|
+
if (firstInstalled.kind === "hook") {
|
|
5950
|
+
const camel = firstInstalled.name.split("-").map((w, i) => i === 0 ? w : w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
5951
|
+
usage = `import { ${camel} } from "@/components/calabasas/${firstInstalled.name}";
|
|
5952
|
+
|
|
5953
|
+
const count = ${camel}("123456789");`;
|
|
5954
|
+
} else {
|
|
5955
|
+
const pascal = toPascalCase(firstInstalled.name);
|
|
5956
|
+
const needsGuild = firstInstalled.requiredSyncTypes.some((t) => t === "channels" || t === "roles" || t === "members");
|
|
5957
|
+
const displayComponents = new Set([
|
|
5958
|
+
"role-badge",
|
|
5959
|
+
"server-info",
|
|
5960
|
+
"permission-viewer",
|
|
5961
|
+
"member-card"
|
|
5962
|
+
]);
|
|
5963
|
+
const isDisplay = displayComponents.has(firstInstalled.name);
|
|
5964
|
+
if (isDisplay) {
|
|
5965
|
+
const propsMap = {
|
|
5966
|
+
"role-badge": ` guildDiscordId="123456789"
|
|
5806
5967
|
roleIds={["role_id_1", "role_id_2"]}`,
|
|
5807
|
-
|
|
5808
|
-
|
|
5968
|
+
"server-info": ` guildDiscordId="123456789"`,
|
|
5969
|
+
"permission-viewer": ` guildDiscordId="123456789"
|
|
5809
5970
|
roleDiscordId="role_id"`,
|
|
5810
|
-
|
|
5971
|
+
"member-card": ` guildDiscordId="123456789"
|
|
5811
5972
|
memberDiscordUserId="user_id"`
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5973
|
+
};
|
|
5974
|
+
const props = propsMap[firstInstalled.name] ?? ` guildDiscordId="123456789"`;
|
|
5975
|
+
usage = `import { ${pascal} } from "@/components/calabasas/${firstInstalled.name}";
|
|
5815
5976
|
|
|
5816
5977
|
<${pascal}
|
|
5817
5978
|
${props}
|
|
5818
5979
|
/>`;
|
|
5819
|
-
|
|
5820
|
-
|
|
5980
|
+
} else if (needsGuild) {
|
|
5981
|
+
usage = `import { ${pascal} } from "@/components/calabasas/${firstInstalled.name}";
|
|
5821
5982
|
|
|
5822
5983
|
<${pascal}
|
|
5823
5984
|
guildDiscordId="123456789"
|
|
5824
5985
|
onValueChange={(id) => console.log(id)}
|
|
5825
5986
|
/>`;
|
|
5826
|
-
|
|
5827
|
-
|
|
5987
|
+
} else {
|
|
5988
|
+
usage = `import { ${pascal} } from "@/components/calabasas/${firstInstalled.name}";
|
|
5828
5989
|
|
|
5829
5990
|
<${pascal}
|
|
5830
5991
|
onValueChange={(id) => console.log(id)}
|
|
5831
5992
|
/>`;
|
|
5993
|
+
}
|
|
5832
5994
|
}
|
|
5833
5995
|
p7.note(usage, "Usage example");
|
|
5834
5996
|
}
|
|
@@ -6354,7 +6516,7 @@ program2.command("logout").description("Clear stored credentials").option("--dev
|
|
|
6354
6516
|
program2.command("init").description("Initialize Calabasas config in your Convex project").action(init);
|
|
6355
6517
|
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);
|
|
6356
6518
|
program2.command("generate").description("Generate type-safe Discord handlers and helpers").option("-o, --output <path>", "Output path", "convex/calabasas/_generated/discord.ts").action(generate);
|
|
6357
|
-
program2.command("skill").description("Generate Calabasas documentation for AI assistants").option("--dev", "Use development environment").option("--prod", "Use production environment").action(skill);
|
|
6519
|
+
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);
|
|
6358
6520
|
program2.command("add [components...]").description("Add Discord UI components to your project").action(add);
|
|
6359
6521
|
program2.command("migrate [name]").description("Run a codemod migration (list available if no name given)").action(migrate);
|
|
6360
6522
|
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);
|