fix: truncate skill command descriptions to 100 chars for Discord (#1018)

* fix: truncate skill command descriptions to 100 chars for Discord

Discord slash commands have a 100 character limit for descriptions.
Skill descriptions were not being truncated, causing command registration
to fail with an empty error from the Discord API.

* style: format

* style: format
This commit is contained in:
Wilkins
2026-01-16 16:01:59 +00:00
committed by GitHub
parent 0d6af15d1c
commit bb14b19922
11 changed files with 83 additions and 66 deletions

View File

@@ -62,9 +62,7 @@ function escapeRegExp(value: string) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function buildSkillCommandDefinitions(
skillCommands?: SkillCommandSpec[],
): ChatCommandDefinition[] {
function buildSkillCommandDefinitions(skillCommands?: SkillCommandSpec[]): ChatCommandDefinition[] {
if (!skillCommands || skillCommands.length === 0) return [];
return skillCommands.map((spec) => ({
key: `skill:${spec.skillName}`,
@@ -77,7 +75,9 @@ function buildSkillCommandDefinitions(
}));
}
export function listChatCommands(params?: { skillCommands?: SkillCommandSpec[] }): ChatCommandDefinition[] {
export function listChatCommands(params?: {
skillCommands?: SkillCommandSpec[];
}): ChatCommandDefinition[] {
if (!params?.skillCommands?.length) return [...CHAT_COMMANDS];
return [...CHAT_COMMANDS, ...buildSkillCommandDefinitions(params.skillCommands)];
}
@@ -98,7 +98,9 @@ export function listChatCommandsForConfig(
return [...base, ...buildSkillCommandDefinitions(params.skillCommands)];
}
export function listNativeCommandSpecs(params?: { skillCommands?: SkillCommandSpec[] }): NativeCommandSpec[] {
export function listNativeCommandSpecs(params?: {
skillCommands?: SkillCommandSpec[];
}): NativeCommandSpec[] {
return listChatCommands({ skillCommands: params?.skillCommands })
.filter((command) => command.scope !== "text" && command.nativeName)
.map((command) => ({

View File

@@ -187,34 +187,34 @@ export async function handleInlineActions(params: {
commandBodyNormalized: inlineCommand.command,
};
const inlineResult = await handleCommands({
ctx,
cfg,
command: inlineCommandContext,
agentId,
directives,
elevated: {
enabled: elevatedEnabled,
allowed: elevatedAllowed,
failures: elevatedFailures,
},
sessionEntry,
sessionStore,
sessionKey,
storePath,
sessionScope,
workspaceDir,
defaultGroupActivation: defaultActivation,
resolvedThinkLevel,
resolvedVerboseLevel: resolvedVerboseLevel ?? "off",
resolvedReasoningLevel,
resolvedElevatedLevel,
resolveDefaultThinkingLevel,
provider,
model,
contextTokens,
isGroup,
skillCommands,
});
ctx,
cfg,
command: inlineCommandContext,
agentId,
directives,
elevated: {
enabled: elevatedEnabled,
allowed: elevatedAllowed,
failures: elevatedFailures,
},
sessionEntry,
sessionStore,
sessionKey,
storePath,
sessionScope,
workspaceDir,
defaultGroupActivation: defaultActivation,
resolvedThinkLevel,
resolvedVerboseLevel: resolvedVerboseLevel ?? "off",
resolvedReasoningLevel,
resolvedElevatedLevel,
resolveDefaultThinkingLevel,
provider,
model,
contextTokens,
isGroup,
skillCommands,
});
if (inlineResult.reply) {
if (!inlineCommand.cleaned) {
typing.cleanup();

View File

@@ -5,9 +5,7 @@ describe("resolveSkillCommandInvocation", () => {
it("matches skill commands and parses args", () => {
const invocation = resolveSkillCommandInvocation({
commandBodyNormalized: "/demo_skill do the thing",
skillCommands: [
{ name: "demo_skill", skillName: "demo-skill", description: "Demo" },
],
skillCommands: [{ name: "demo_skill", skillName: "demo-skill", description: "Demo" }],
});
expect(invocation?.command.skillName).toBe("demo-skill");
expect(invocation?.args).toBe("do the thing");
@@ -16,9 +14,7 @@ describe("resolveSkillCommandInvocation", () => {
it("returns null for unknown commands", () => {
const invocation = resolveSkillCommandInvocation({
commandBodyNormalized: "/unknown arg",
skillCommands: [
{ name: "demo_skill", skillName: "demo-skill", description: "Demo" },
],
skillCommands: [{ name: "demo_skill", skillName: "demo-skill", description: "Demo" }],
});
expect(invocation).toBeNull();
});

View File

@@ -1,9 +1,6 @@
import type { ClawdbotConfig } from "../config/config.js";
import { getRemoteSkillEligibility } from "../infra/skills-remote.js";
import {
buildWorkspaceSkillCommandSpecs,
type SkillCommandSpec,
} from "../agents/skills.js";
import { buildWorkspaceSkillCommandSpecs, type SkillCommandSpec } from "../agents/skills.js";
import { listChatCommands } from "./commands-registry.js";
function resolveReservedCommandNames(): Set<string> {
@@ -42,9 +39,7 @@ export function resolveSkillCommandInvocation(params: {
if (!match) return null;
const commandName = match[1]?.trim().toLowerCase();
if (!commandName) return null;
const command = params.skillCommands.find(
(entry) => entry.name.toLowerCase() === commandName,
);
const command = params.skillCommands.find((entry) => entry.name.toLowerCase() === commandName);
if (!command) return null;
const args = match[2]?.trim();
return { command, args: args || undefined };