@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/CHANGELOG.md +18 -0
- package/LICENSE +21 -0
- package/README.md +281 -0
- package/dist/index.d.mts +390 -0
- package/dist/index.d.ts +390 -0
- package/dist/index.js +1006 -0
- package/dist/index.mjs +955 -0
- package/package.json +62 -0
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
|
+
});
|