@affectively/slash-commands 1.0.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 ADDED
@@ -0,0 +1,1006 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ SlashCommandRegistry: () => SlashCommandRegistry,
24
+ createAPIExecutor: () => createAPIExecutor,
25
+ executeSlashCommand: () => executeSlashCommand,
26
+ extractCategory: () => extractCategory,
27
+ extractCommandName: () => extractCommandName,
28
+ extractParametersFromJsonSchema: () => extractParametersFromJsonSchema,
29
+ extractParametersFromZod: () => extractParametersFromZod,
30
+ formatCommand: () => formatCommand,
31
+ fuzzyMatch: () => fuzzyMatch,
32
+ generateAliases: () => generateAliases,
33
+ generateExamples: () => generateExamples,
34
+ generateHelpText: () => generateHelpText,
35
+ generateSlashCommand: () => generateSlashCommand,
36
+ generateSlashCommands: () => generateSlashCommands,
37
+ getAllSlashCommands: () => getAllSlashCommands,
38
+ getAutocompleteSuggestions: () => getAutocompleteSuggestions,
39
+ getPartialCommand: () => getPartialCommand,
40
+ getReplacementRange: () => getReplacementRange,
41
+ getSlashCommand: () => getSlashCommand,
42
+ getSuggestionsGroupedByCategory: () => getSuggestionsGroupedByCategory,
43
+ hasSlashCommand: () => hasSlashCommand,
44
+ isSlashCommand: () => isSlashCommand,
45
+ parseSlashCommand: () => parseSlashCommand,
46
+ shouldShowAutocomplete: () => shouldShowAutocomplete,
47
+ slashCommandRegistry: () => slashCommandRegistry
48
+ });
49
+ module.exports = __toCommonJS(index_exports);
50
+
51
+ // src/generator.ts
52
+ var import_zod = require("zod");
53
+ function extractCategory(toolName) {
54
+ const parts = toolName.split("_");
55
+ if (parts.length > 1) {
56
+ return parts[0];
57
+ }
58
+ return toolName;
59
+ }
60
+ function extractParametersFromZod(schema) {
61
+ const parameters = [];
62
+ if (schema instanceof import_zod.z.ZodObject) {
63
+ const shape = schema.shape;
64
+ for (const [name, fieldSchema] of Object.entries(shape)) {
65
+ const param = extractSingleParameter(
66
+ name,
67
+ fieldSchema,
68
+ schema
69
+ );
70
+ if (param) {
71
+ parameters.push(param);
72
+ }
73
+ }
74
+ }
75
+ return parameters;
76
+ }
77
+ function extractSingleParameter(name, fieldSchema, _parentSchema) {
78
+ let innerSchema = fieldSchema;
79
+ let isOptional = false;
80
+ let defaultValue = void 0;
81
+ if (innerSchema instanceof import_zod.z.ZodOptional) {
82
+ isOptional = true;
83
+ innerSchema = innerSchema._def.innerType;
84
+ }
85
+ if (innerSchema instanceof import_zod.z.ZodNullable) {
86
+ isOptional = true;
87
+ innerSchema = innerSchema._def.innerType;
88
+ }
89
+ if (innerSchema instanceof import_zod.z.ZodDefault) {
90
+ defaultValue = innerSchema._def.defaultValue();
91
+ innerSchema = innerSchema._def.innerType;
92
+ }
93
+ const description = innerSchema._def.description || fieldSchema._def.description || "";
94
+ const typeInfo = getZodType(innerSchema);
95
+ return {
96
+ name,
97
+ type: typeInfo.type,
98
+ description,
99
+ required: !isOptional && defaultValue === void 0,
100
+ defaultValue,
101
+ enumValues: typeInfo.enumValues
102
+ };
103
+ }
104
+ function getZodType(schema) {
105
+ if (schema instanceof import_zod.z.ZodString) {
106
+ return { type: "string" };
107
+ }
108
+ if (schema instanceof import_zod.z.ZodNumber) {
109
+ return { type: "number" };
110
+ }
111
+ if (schema instanceof import_zod.z.ZodBoolean) {
112
+ return { type: "boolean" };
113
+ }
114
+ if (schema instanceof import_zod.z.ZodEnum) {
115
+ return {
116
+ type: "enum",
117
+ enumValues: schema._def.values
118
+ };
119
+ }
120
+ if (schema instanceof import_zod.z.ZodArray) {
121
+ return { type: "array" };
122
+ }
123
+ if (schema instanceof import_zod.z.ZodObject) {
124
+ return { type: "object" };
125
+ }
126
+ if (schema instanceof import_zod.z.ZodNativeEnum) {
127
+ const values = Object.values(schema._def.values).filter(
128
+ (v) => typeof v === "string"
129
+ );
130
+ return { type: "enum", enumValues: values };
131
+ }
132
+ return { type: "string" };
133
+ }
134
+ function generateHelpText(name, description, parameters) {
135
+ const lines = [];
136
+ lines.push(`/${name}`);
137
+ lines.push("");
138
+ lines.push(description);
139
+ if (parameters.length > 0) {
140
+ lines.push("");
141
+ lines.push("Parameters:");
142
+ for (const param of parameters) {
143
+ const required = param.required ? "(required)" : "(optional)";
144
+ const type = param.enumValues ? `[${param.enumValues.join("|")}]` : `<${param.type}>`;
145
+ const defaultStr = param.defaultValue !== void 0 ? ` (default: ${JSON.stringify(param.defaultValue)})` : "";
146
+ lines.push(` ${param.name} ${type} ${required}${defaultStr}`);
147
+ if (param.description) {
148
+ lines.push(` ${param.description}`);
149
+ }
150
+ }
151
+ }
152
+ return lines.join("\n");
153
+ }
154
+ function generateExamples(name, parameters) {
155
+ const examples = [];
156
+ examples.push(`/${name}`);
157
+ const stringParams = parameters.filter(
158
+ (p) => p.type === "string" && !p.enumValues
159
+ );
160
+ const enumParams = parameters.filter(
161
+ (p) => p.type === "enum" && p.enumValues
162
+ );
163
+ const numberParams = parameters.filter((p) => p.type === "number");
164
+ if (enumParams.length > 0) {
165
+ const param = enumParams[0];
166
+ const value = param.enumValues?.[0] || "value";
167
+ examples.push(`/${name} ${param.name}=${value}`);
168
+ }
169
+ if (stringParams.length > 0) {
170
+ const param = stringParams[0];
171
+ examples.push(`/${name} ${param.name}="example"`);
172
+ }
173
+ const limitParam = numberParams.find((p) => p.name === "limit");
174
+ if (limitParam) {
175
+ examples.push(`/${name} limit=10`);
176
+ }
177
+ return examples.slice(0, 3);
178
+ }
179
+ function generateSlashCommand(tool, tierOverride) {
180
+ const { name, description, inputSchema } = tool;
181
+ const isZodSchema = inputSchema instanceof import_zod.z.ZodType;
182
+ const parameters = isZodSchema ? extractParametersFromZod(inputSchema) : extractParametersFromJsonSchema(inputSchema);
183
+ const category = extractCategory(name);
184
+ const requiredTier = tierOverride || tool.requiredTier || "free";
185
+ const helpText = generateHelpText(name, description, parameters);
186
+ const examples = generateExamples(name, parameters);
187
+ return {
188
+ name,
189
+ description,
190
+ schema: isZodSchema ? inputSchema : createSchemaFromJsonSchema(inputSchema),
191
+ category,
192
+ requiredTier,
193
+ parameters,
194
+ helpText,
195
+ examples
196
+ };
197
+ }
198
+ function extractParametersFromJsonSchema(jsonSchema) {
199
+ const parameters = [];
200
+ if (jsonSchema.type !== "object" || !jsonSchema.properties) {
201
+ return parameters;
202
+ }
203
+ const properties = jsonSchema.properties;
204
+ const required = jsonSchema.required || [];
205
+ for (const [name, propSchema] of Object.entries(properties)) {
206
+ const param = {
207
+ name,
208
+ type: mapJsonSchemaType(propSchema.type),
209
+ description: propSchema.description || "",
210
+ required: required.includes(name),
211
+ defaultValue: propSchema.default,
212
+ enumValues: propSchema.enum
213
+ };
214
+ parameters.push(param);
215
+ }
216
+ return parameters;
217
+ }
218
+ function mapJsonSchemaType(jsonType) {
219
+ switch (jsonType) {
220
+ case "string":
221
+ return "string";
222
+ case "number":
223
+ case "integer":
224
+ return "number";
225
+ case "boolean":
226
+ return "boolean";
227
+ case "array":
228
+ return "array";
229
+ case "object":
230
+ return "object";
231
+ default:
232
+ return "string";
233
+ }
234
+ }
235
+ function createSchemaFromJsonSchema(jsonSchema) {
236
+ if (jsonSchema.type !== "object" || !jsonSchema.properties) {
237
+ return import_zod.z.object({});
238
+ }
239
+ const properties = jsonSchema.properties;
240
+ const required = new Set(jsonSchema.required || []);
241
+ const shape = {};
242
+ for (const [name, propSchema] of Object.entries(properties)) {
243
+ let fieldSchema = createFieldSchema(propSchema);
244
+ if (!required.has(name)) {
245
+ fieldSchema = fieldSchema.optional();
246
+ }
247
+ if (propSchema.description) {
248
+ fieldSchema = fieldSchema.describe(propSchema.description);
249
+ }
250
+ shape[name] = fieldSchema;
251
+ }
252
+ return import_zod.z.object(shape);
253
+ }
254
+ function createFieldSchema(propSchema) {
255
+ const type = propSchema.type;
256
+ if (propSchema.enum && Array.isArray(propSchema.enum)) {
257
+ return import_zod.z.enum(propSchema.enum);
258
+ }
259
+ switch (type) {
260
+ case "string":
261
+ return import_zod.z.string();
262
+ case "number":
263
+ case "integer":
264
+ return import_zod.z.number();
265
+ case "boolean":
266
+ return import_zod.z.boolean();
267
+ case "array":
268
+ return import_zod.z.array(import_zod.z.unknown());
269
+ case "object":
270
+ return import_zod.z.record(import_zod.z.unknown());
271
+ default:
272
+ return import_zod.z.unknown();
273
+ }
274
+ }
275
+ function generateSlashCommands(tools) {
276
+ return tools.map((tool) => generateSlashCommand(tool));
277
+ }
278
+ function generateAliases(name) {
279
+ const aliases = [];
280
+ const parts = name.split("_");
281
+ if (parts.length > 1) {
282
+ const acronym = parts.map((p) => p[0]).join("");
283
+ aliases.push(acronym);
284
+ }
285
+ const kebab = name.replace(/_/g, "-");
286
+ if (kebab !== name) {
287
+ aliases.push(kebab);
288
+ }
289
+ return aliases;
290
+ }
291
+
292
+ // src/parser.ts
293
+ var DEFAULT_OPTIONS = {
294
+ allowUnknownArgs: false,
295
+ coerceTypes: true,
296
+ argSeparator: " "
297
+ };
298
+ function isSlashCommand(input) {
299
+ return input.trim().startsWith("/");
300
+ }
301
+ function extractCommandName(input) {
302
+ const trimmed = input.trim();
303
+ if (!trimmed.startsWith("/")) {
304
+ return "";
305
+ }
306
+ const withoutSlash = trimmed.slice(1);
307
+ const match = withoutSlash.match(/^([\w-]+)/);
308
+ return match ? match[1] : "";
309
+ }
310
+ function parseSlashCommand(input, registry, options = {}) {
311
+ const opts = { ...DEFAULT_OPTIONS, ...options };
312
+ const trimmed = input.trim();
313
+ if (!trimmed.startsWith("/")) {
314
+ return {
315
+ command: "",
316
+ args: {},
317
+ raw: input,
318
+ found: false,
319
+ errors: ["Input must start with /"]
320
+ };
321
+ }
322
+ const withoutSlash = trimmed.slice(1);
323
+ const tokens = tokenize(withoutSlash);
324
+ if (tokens.length === 0) {
325
+ return {
326
+ command: "",
327
+ args: {},
328
+ raw: input,
329
+ found: false,
330
+ errors: ["No command specified"]
331
+ };
332
+ }
333
+ const { command, argTokens } = resolveCommand(tokens, registry);
334
+ const { args, errors } = parseArguments(argTokens, opts);
335
+ const definition = registry?.get(command);
336
+ const found = registry ? !!definition : true;
337
+ const validationErrors = definition ? validateArgs(args, definition, opts) : [];
338
+ return {
339
+ command,
340
+ args,
341
+ raw: input,
342
+ found,
343
+ errors: [...errors, ...validationErrors].length > 0 ? [...errors, ...validationErrors] : void 0,
344
+ definition
345
+ };
346
+ }
347
+ function tokenize(input) {
348
+ const tokens = [];
349
+ let current = "";
350
+ let inQuote = false;
351
+ let quoteChar = "";
352
+ for (let i = 0; i < input.length; i++) {
353
+ const char = input[i];
354
+ if (inQuote) {
355
+ if (char === quoteChar) {
356
+ inQuote = false;
357
+ quoteChar = "";
358
+ } else {
359
+ current += char;
360
+ }
361
+ } else if (char === '"' || char === "'") {
362
+ inQuote = true;
363
+ quoteChar = char;
364
+ } else if (char === " " || char === " ") {
365
+ if (current) {
366
+ tokens.push(current);
367
+ current = "";
368
+ }
369
+ } else {
370
+ current += char;
371
+ }
372
+ }
373
+ if (current) {
374
+ tokens.push(current);
375
+ }
376
+ return tokens;
377
+ }
378
+ function resolveCommand(tokens, registry) {
379
+ const firstToken = tokens[0];
380
+ if (registry?.has(firstToken)) {
381
+ return { command: firstToken, argTokens: tokens.slice(1) };
382
+ }
383
+ if (tokens.length > 1) {
384
+ const combined = `${firstToken}_${tokens[1]}`;
385
+ if (registry?.has(combined)) {
386
+ return { command: combined, argTokens: tokens.slice(2) };
387
+ }
388
+ }
389
+ const snakeCase = firstToken.replace(/-/g, "_");
390
+ if (registry?.has(snakeCase)) {
391
+ return { command: snakeCase, argTokens: tokens.slice(1) };
392
+ }
393
+ return { command: firstToken, argTokens: tokens.slice(1) };
394
+ }
395
+ function parseArguments(tokens, opts) {
396
+ const args = {};
397
+ const errors = [];
398
+ let i = 0;
399
+ while (i < tokens.length) {
400
+ const token = tokens[i];
401
+ if (token.startsWith("-")) {
402
+ const eqIndex2 = token.indexOf("=");
403
+ if (eqIndex2 > 0) {
404
+ const key2 = token.slice(token.startsWith("--") ? 2 : 1, eqIndex2);
405
+ const value = token.slice(eqIndex2 + 1);
406
+ args[key2] = opts.coerceTypes ? coerceValue(value) : value;
407
+ i++;
408
+ continue;
409
+ }
410
+ const key = token.slice(token.startsWith("--") ? 2 : 1);
411
+ if (i + 1 < tokens.length && !tokens[i + 1].startsWith("-")) {
412
+ const value = tokens[i + 1];
413
+ args[key] = opts.coerceTypes ? coerceValue(value) : value;
414
+ i += 2;
415
+ } else {
416
+ args[key] = true;
417
+ i++;
418
+ }
419
+ continue;
420
+ }
421
+ const eqIndex = token.indexOf("=");
422
+ if (eqIndex > 0) {
423
+ const key = token.slice(0, eqIndex);
424
+ const value = token.slice(eqIndex + 1);
425
+ args[key] = opts.coerceTypes ? coerceValue(value) : value;
426
+ i++;
427
+ continue;
428
+ }
429
+ errors.push(`Unknown argument format: ${token}`);
430
+ i++;
431
+ }
432
+ return { args, errors };
433
+ }
434
+ function coerceValue(value) {
435
+ if (value.toLowerCase() === "true") return true;
436
+ if (value.toLowerCase() === "false") return false;
437
+ if (value.toLowerCase() === "null") return null;
438
+ if (value.toLowerCase() === "undefined") return void 0;
439
+ if (/^-?\d+$/.test(value)) {
440
+ return parseInt(value, 10);
441
+ }
442
+ if (/^-?\d+\.\d+$/.test(value)) {
443
+ return parseFloat(value);
444
+ }
445
+ if (value.startsWith("[") && value.endsWith("]") || value.startsWith("{") && value.endsWith("}")) {
446
+ try {
447
+ return JSON.parse(value);
448
+ } catch {
449
+ }
450
+ }
451
+ return value;
452
+ }
453
+ function validateArgs(args, definition, opts) {
454
+ const errors = [];
455
+ const paramNames = new Set(definition.parameters.map((p) => p.name));
456
+ if (!opts.allowUnknownArgs) {
457
+ for (const key of Object.keys(args)) {
458
+ if (!paramNames.has(key)) {
459
+ errors.push(`Unknown argument: ${key}`);
460
+ }
461
+ }
462
+ }
463
+ for (const param of definition.parameters) {
464
+ if (param.required && !(param.name in args)) {
465
+ errors.push(`Missing required argument: ${param.name}`);
466
+ }
467
+ }
468
+ for (const param of definition.parameters) {
469
+ if (param.enumValues && param.name in args) {
470
+ const value = args[param.name];
471
+ if (typeof value === "string" && !param.enumValues.includes(value)) {
472
+ errors.push(
473
+ `Invalid value for ${param.name}: '${value}'. Expected one of: ${param.enumValues.join(", ")}`
474
+ );
475
+ }
476
+ }
477
+ }
478
+ for (const param of definition.parameters) {
479
+ if (param.name in args) {
480
+ const value = args[param.name];
481
+ const typeError = validateType(value, param.type);
482
+ if (typeError) {
483
+ errors.push(`${param.name}: ${typeError}`);
484
+ }
485
+ }
486
+ }
487
+ return errors;
488
+ }
489
+ function validateType(value, expectedType) {
490
+ const actualType = typeof value;
491
+ switch (expectedType) {
492
+ case "string":
493
+ if (actualType !== "string") {
494
+ return `expected string, got ${actualType}`;
495
+ }
496
+ break;
497
+ case "number":
498
+ if (actualType !== "number" || isNaN(value)) {
499
+ return `expected number, got ${actualType}`;
500
+ }
501
+ break;
502
+ case "boolean":
503
+ if (actualType !== "boolean") {
504
+ return `expected boolean, got ${actualType}`;
505
+ }
506
+ break;
507
+ case "array":
508
+ if (!Array.isArray(value)) {
509
+ return `expected array, got ${actualType}`;
510
+ }
511
+ break;
512
+ case "object":
513
+ if (actualType !== "object" || value === null || Array.isArray(value)) {
514
+ return `expected object, got ${actualType}`;
515
+ }
516
+ break;
517
+ }
518
+ return null;
519
+ }
520
+ function formatCommand(command, args) {
521
+ const parts = [`/${command}`];
522
+ for (const [key, value] of Object.entries(args)) {
523
+ if (typeof value === "string" && value.includes(" ")) {
524
+ parts.push(`${key}="${value}"`);
525
+ } else if (typeof value === "object") {
526
+ parts.push(`${key}=${JSON.stringify(value)}`);
527
+ } else {
528
+ parts.push(`${key}=${value}`);
529
+ }
530
+ }
531
+ return parts.join(" ");
532
+ }
533
+ function getPartialCommand(input) {
534
+ const trimmed = input.trim();
535
+ if (!trimmed.startsWith("/")) {
536
+ return { command: "", isComplete: false };
537
+ }
538
+ const withoutSlash = trimmed.slice(1);
539
+ const spaceIndex = withoutSlash.indexOf(" ");
540
+ if (spaceIndex === -1) {
541
+ return { command: withoutSlash, isComplete: false };
542
+ }
543
+ const command = withoutSlash.slice(0, spaceIndex);
544
+ const rest = withoutSlash.slice(spaceIndex + 1).trim();
545
+ const lastToken = rest.split(" ").pop() || "";
546
+ const isTypingArg = !lastToken.includes("=") || lastToken.endsWith("=");
547
+ return {
548
+ command,
549
+ isComplete: true,
550
+ partialArg: isTypingArg ? lastToken : void 0
551
+ };
552
+ }
553
+
554
+ // src/registry.ts
555
+ var SlashCommandRegistry = class {
556
+ constructor() {
557
+ this.commands = /* @__PURE__ */ new Map();
558
+ this.aliases = /* @__PURE__ */ new Map();
559
+ // alias -> command name
560
+ this.categories = /* @__PURE__ */ new Set();
561
+ }
562
+ /**
563
+ * Register a single slash command
564
+ */
565
+ register(command) {
566
+ this.commands.set(command.name, command);
567
+ this.categories.add(command.category);
568
+ if (command.aliases) {
569
+ for (const alias of command.aliases) {
570
+ this.aliases.set(alias, command.name);
571
+ }
572
+ }
573
+ const autoAliases = generateAliases(command.name);
574
+ for (const alias of autoAliases) {
575
+ if (!this.aliases.has(alias) && !this.commands.has(alias)) {
576
+ this.aliases.set(alias, command.name);
577
+ }
578
+ }
579
+ }
580
+ /**
581
+ * Register multiple commands at once
582
+ */
583
+ registerAll(commands) {
584
+ for (const command of commands) {
585
+ this.register(command);
586
+ }
587
+ }
588
+ /**
589
+ * Register commands from tool definitions
590
+ */
591
+ registerFromTools(tools) {
592
+ for (const tool of tools) {
593
+ const command = generateSlashCommand(tool);
594
+ this.register(command);
595
+ }
596
+ }
597
+ /**
598
+ * Get a command by name or alias
599
+ */
600
+ get(nameOrAlias) {
601
+ const direct = this.commands.get(nameOrAlias);
602
+ if (direct) return direct;
603
+ const resolvedName = this.aliases.get(nameOrAlias);
604
+ if (resolvedName) {
605
+ return this.commands.get(resolvedName);
606
+ }
607
+ const snakeCase = nameOrAlias.replace(/-/g, "_");
608
+ return this.commands.get(snakeCase);
609
+ }
610
+ /**
611
+ * Check if a command exists
612
+ */
613
+ has(nameOrAlias) {
614
+ return this.get(nameOrAlias) !== void 0;
615
+ }
616
+ /**
617
+ * Get all registered commands
618
+ */
619
+ getAll() {
620
+ return Array.from(this.commands.values());
621
+ }
622
+ /**
623
+ * Get commands filtered by category
624
+ */
625
+ getByCategory(category) {
626
+ return this.getAll().filter((cmd) => cmd.category === category);
627
+ }
628
+ /**
629
+ * Get commands available for a subscription tier
630
+ */
631
+ getForTier(tier) {
632
+ const tierOrder = [
633
+ "free",
634
+ "premium",
635
+ "ultra-premium",
636
+ "enterprise"
637
+ ];
638
+ const tierIndex = tierOrder.indexOf(tier);
639
+ return this.getAll().filter((cmd) => {
640
+ const cmdTierIndex = tierOrder.indexOf(cmd.requiredTier);
641
+ return cmdTierIndex <= tierIndex;
642
+ });
643
+ }
644
+ /**
645
+ * Get all categories
646
+ */
647
+ getCategories() {
648
+ return Array.from(this.categories).sort();
649
+ }
650
+ /**
651
+ * Get command count by category
652
+ */
653
+ getCountByCategory() {
654
+ const counts = {};
655
+ for (const category of this.categories) {
656
+ counts[category] = this.getByCategory(category).length;
657
+ }
658
+ return counts;
659
+ }
660
+ /**
661
+ * Resolve an alias to the canonical command name
662
+ */
663
+ resolveAlias(alias) {
664
+ return this.aliases.get(alias);
665
+ }
666
+ /**
667
+ * Get the underlying Map for direct access
668
+ */
669
+ getMap() {
670
+ return this.commands;
671
+ }
672
+ /**
673
+ * Clear all registered commands
674
+ */
675
+ clear() {
676
+ this.commands.clear();
677
+ this.aliases.clear();
678
+ this.categories.clear();
679
+ }
680
+ /**
681
+ * Get total command count
682
+ */
683
+ get size() {
684
+ return this.commands.size;
685
+ }
686
+ };
687
+ var slashCommandRegistry = new SlashCommandRegistry();
688
+ async function executeSlashCommand(input, context, executor) {
689
+ const startTime = Date.now();
690
+ const parsed = parseSlashCommand(input, slashCommandRegistry.getMap());
691
+ if (!parsed.found) {
692
+ return {
693
+ success: false,
694
+ error: `Unknown command: ${parsed.command}`,
695
+ executionTimeMs: Date.now() - startTime,
696
+ command: parsed.command,
697
+ args: parsed.args
698
+ };
699
+ }
700
+ if (parsed.errors && parsed.errors.length > 0) {
701
+ return {
702
+ success: false,
703
+ error: parsed.errors.join("; "),
704
+ executionTimeMs: Date.now() - startTime,
705
+ command: parsed.command,
706
+ args: parsed.args
707
+ };
708
+ }
709
+ const definition = parsed.definition;
710
+ const tierOrder = [
711
+ "free",
712
+ "premium",
713
+ "ultra-premium",
714
+ "enterprise"
715
+ ];
716
+ const userTierIndex = tierOrder.indexOf(context.userTier);
717
+ const requiredTierIndex = tierOrder.indexOf(definition.requiredTier);
718
+ if (requiredTierIndex > userTierIndex) {
719
+ return {
720
+ success: false,
721
+ error: `This command requires ${definition.requiredTier} subscription`,
722
+ executionTimeMs: Date.now() - startTime,
723
+ command: parsed.command,
724
+ args: parsed.args
725
+ };
726
+ }
727
+ try {
728
+ const data = await executor(parsed.command, parsed.args, context);
729
+ return {
730
+ success: true,
731
+ data,
732
+ executionTimeMs: Date.now() - startTime,
733
+ command: parsed.command,
734
+ args: parsed.args
735
+ };
736
+ } catch (error) {
737
+ return {
738
+ success: false,
739
+ error: error instanceof Error ? error.message : String(error),
740
+ executionTimeMs: Date.now() - startTime,
741
+ command: parsed.command,
742
+ args: parsed.args
743
+ };
744
+ }
745
+ }
746
+ function createAPIExecutor(apiBaseUrl, authToken) {
747
+ return async (command, args, context) => {
748
+ const url = `${apiBaseUrl}/api/slash-command`;
749
+ const token = authToken || context.authToken;
750
+ const response = await fetch(url, {
751
+ method: "POST",
752
+ headers: {
753
+ "Content-Type": "application/json",
754
+ ...token ? { Authorization: `Bearer ${token}` } : {}
755
+ },
756
+ body: JSON.stringify({
757
+ command,
758
+ args,
759
+ conversationId: context.conversationId
760
+ })
761
+ });
762
+ if (!response.ok) {
763
+ const errorText = await response.text();
764
+ throw new Error(`API error: ${response.status} - ${errorText}`);
765
+ }
766
+ return response.json();
767
+ };
768
+ }
769
+ function getAllSlashCommands() {
770
+ return slashCommandRegistry.getAll();
771
+ }
772
+ function getSlashCommand(name) {
773
+ return slashCommandRegistry.get(name);
774
+ }
775
+ function hasSlashCommand(name) {
776
+ return slashCommandRegistry.has(name);
777
+ }
778
+
779
+ // src/autocomplete.ts
780
+ var DEFAULT_OPTIONS2 = {
781
+ maxSuggestions: 10,
782
+ userTier: "free",
783
+ includeHidden: false,
784
+ minScore: 0
785
+ };
786
+ function fuzzyMatch(query, target) {
787
+ if (!query) return 50;
788
+ if (query === target) return 100;
789
+ const q = query.toLowerCase();
790
+ const t = target.toLowerCase();
791
+ if (t.startsWith(q)) {
792
+ return 90 + q.length / t.length * 10;
793
+ }
794
+ const words = t.split(/[_\-\s]/);
795
+ const initials = words.map((w) => w[0]).join("");
796
+ if (initials.startsWith(q)) {
797
+ return 80 + q.length / initials.length * 10;
798
+ }
799
+ if (t.includes(q)) {
800
+ const position = t.indexOf(q);
801
+ const positionScore = Math.max(0, 70 - position * 2);
802
+ return positionScore;
803
+ }
804
+ let qIndex = 0;
805
+ let matchCount = 0;
806
+ let consecutiveBonus = 0;
807
+ let lastMatchIndex = -2;
808
+ for (let i = 0; i < t.length && qIndex < q.length; i++) {
809
+ if (t[i] === q[qIndex]) {
810
+ matchCount++;
811
+ if (i === lastMatchIndex + 1) {
812
+ consecutiveBonus += 5;
813
+ }
814
+ lastMatchIndex = i;
815
+ qIndex++;
816
+ }
817
+ }
818
+ if (qIndex === q.length) {
819
+ const matchRatio = matchCount / q.length;
820
+ const lengthPenalty = Math.max(0, 1 - (t.length - q.length) / 20);
821
+ return Math.min(
822
+ 60,
823
+ 30 + matchRatio * 20 + consecutiveBonus + lengthPenalty * 10
824
+ );
825
+ }
826
+ return 0;
827
+ }
828
+ function getAutocompleteSuggestions(input, options = {}, registry = slashCommandRegistry) {
829
+ const opts = { ...DEFAULT_OPTIONS2, ...options };
830
+ const partial = getPartialCommand(input);
831
+ const tierOrder = [
832
+ "free",
833
+ "premium",
834
+ "ultra-premium",
835
+ "enterprise"
836
+ ];
837
+ const userTierIndex = tierOrder.indexOf(opts.userTier);
838
+ const allCommands = registry.getAll().filter((cmd) => {
839
+ if (cmd.hidden && !opts.includeHidden) return false;
840
+ return true;
841
+ });
842
+ if (!partial.isComplete) {
843
+ return suggestCommands(
844
+ partial.command,
845
+ allCommands,
846
+ opts,
847
+ userTierIndex,
848
+ tierOrder
849
+ );
850
+ }
851
+ const command = registry.get(partial.command);
852
+ if (command && partial.partialArg !== void 0) {
853
+ return suggestArguments(
854
+ command,
855
+ partial.partialArg,
856
+ opts,
857
+ userTierIndex,
858
+ tierOrder
859
+ );
860
+ }
861
+ if (command) {
862
+ return suggestArguments(command, "", opts, userTierIndex, tierOrder);
863
+ }
864
+ return [];
865
+ }
866
+ function suggestCommands(query, commands, opts, userTierIndex, tierOrder) {
867
+ const suggestions = [];
868
+ for (const cmd of commands) {
869
+ const score = fuzzyMatch(query, cmd.name);
870
+ if (score < opts.minScore) continue;
871
+ const cmdTierIndex = tierOrder.indexOf(cmd.requiredTier);
872
+ const hasAccess = cmdTierIndex <= userTierIndex;
873
+ suggestions.push({
874
+ command: cmd.name,
875
+ displayText: `/${cmd.name}`,
876
+ description: truncateDescription(cmd.description),
877
+ matchScore: score,
878
+ category: cmd.category,
879
+ hasAccess
880
+ });
881
+ }
882
+ suggestions.sort((a, b) => {
883
+ if (a.hasAccess !== b.hasAccess) {
884
+ return a.hasAccess ? -1 : 1;
885
+ }
886
+ if ((b.matchScore ?? 0) !== (a.matchScore ?? 0)) {
887
+ return (b.matchScore ?? 0) - (a.matchScore ?? 0);
888
+ }
889
+ return a.command.localeCompare(b.command);
890
+ });
891
+ return suggestions.slice(0, opts.maxSuggestions);
892
+ }
893
+ function suggestArguments(command, query, opts, userTierIndex, tierOrder) {
894
+ const suggestions = [];
895
+ const cmdTierIndex = tierOrder.indexOf(command.requiredTier);
896
+ const hasAccess = cmdTierIndex <= userTierIndex;
897
+ const eqIndex = query.indexOf("=");
898
+ if (eqIndex > 0) {
899
+ const argName = query.slice(0, eqIndex).replace(/^-+/, "");
900
+ const valuePrefix = query.slice(eqIndex + 1);
901
+ const param = command.parameters.find((p) => p.name === argName);
902
+ if (param?.enumValues) {
903
+ for (const value of param.enumValues) {
904
+ const score = fuzzyMatch(valuePrefix, value);
905
+ if (score < opts.minScore) continue;
906
+ suggestions.push({
907
+ command: `${argName}=${value}`,
908
+ displayText: `${argName}=${value}`,
909
+ description: `Set ${argName} to ${value}`,
910
+ matchScore: score,
911
+ category: "value",
912
+ hasAccess
913
+ });
914
+ }
915
+ }
916
+ } else {
917
+ const argQuery = query.replace(/^-+/, "");
918
+ for (const param of command.parameters) {
919
+ const score = fuzzyMatch(argQuery, param.name);
920
+ if (score < opts.minScore) continue;
921
+ const requiredTag = param.required ? " (required)" : "";
922
+ const typeTag = param.enumValues ? ` [${param.enumValues.slice(0, 3).join("|")}${param.enumValues.length > 3 ? "..." : ""}]` : ` <${param.type}>`;
923
+ suggestions.push({
924
+ command: param.name,
925
+ displayText: `${param.name}${typeTag}`,
926
+ description: param.description || `${param.name}${requiredTag}`,
927
+ matchScore: score,
928
+ category: "argument",
929
+ hasAccess
930
+ });
931
+ }
932
+ }
933
+ suggestions.sort((a, b) => {
934
+ if ((b.matchScore ?? 0) !== (a.matchScore ?? 0)) {
935
+ return (b.matchScore ?? 0) - (a.matchScore ?? 0);
936
+ }
937
+ return a.command.localeCompare(b.command);
938
+ });
939
+ return suggestions.slice(0, opts.maxSuggestions);
940
+ }
941
+ function truncateDescription(description, maxLength = 80) {
942
+ const firstLine = description.split("\n")[0];
943
+ const firstSentence = firstLine.split(/[.!?]/)[0];
944
+ const text = firstSentence || firstLine;
945
+ if (text.length <= maxLength) {
946
+ return text;
947
+ }
948
+ return text.slice(0, maxLength - 3) + "...";
949
+ }
950
+ function getSuggestionsGroupedByCategory(input, options = {}, registry = slashCommandRegistry) {
951
+ const suggestions = getAutocompleteSuggestions(input, options, registry);
952
+ const grouped = {};
953
+ for (const suggestion of suggestions) {
954
+ if (!grouped[suggestion.category]) {
955
+ grouped[suggestion.category] = [];
956
+ }
957
+ grouped[suggestion.category].push(suggestion);
958
+ }
959
+ return grouped;
960
+ }
961
+ function shouldShowAutocomplete(input) {
962
+ const trimmed = input.trim();
963
+ if (!trimmed.startsWith("/")) {
964
+ return false;
965
+ }
966
+ return true;
967
+ }
968
+ function getReplacementRange(input) {
969
+ const partial = getPartialCommand(input);
970
+ if (!partial.isComplete) {
971
+ return { start: 0, end: input.length };
972
+ }
973
+ if (partial.partialArg !== void 0) {
974
+ const start = input.lastIndexOf(partial.partialArg);
975
+ return { start, end: input.length };
976
+ }
977
+ return { start: input.length, end: input.length };
978
+ }
979
+ // Annotate the CommonJS export names for ESM import in node:
980
+ 0 && (module.exports = {
981
+ SlashCommandRegistry,
982
+ createAPIExecutor,
983
+ executeSlashCommand,
984
+ extractCategory,
985
+ extractCommandName,
986
+ extractParametersFromJsonSchema,
987
+ extractParametersFromZod,
988
+ formatCommand,
989
+ fuzzyMatch,
990
+ generateAliases,
991
+ generateExamples,
992
+ generateHelpText,
993
+ generateSlashCommand,
994
+ generateSlashCommands,
995
+ getAllSlashCommands,
996
+ getAutocompleteSuggestions,
997
+ getPartialCommand,
998
+ getReplacementRange,
999
+ getSlashCommand,
1000
+ getSuggestionsGroupedByCategory,
1001
+ hasSlashCommand,
1002
+ isSlashCommand,
1003
+ parseSlashCommand,
1004
+ shouldShowAutocomplete,
1005
+ slashCommandRegistry
1006
+ });