Files
openclaw/src/auto-reply/skill-commands-base.ts
2026-04-07 17:50:37 +01:00

100 lines
3.0 KiB
TypeScript

import type { SkillCommandSpec } from "../agents/skills.js";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalLowercaseString,
} from "../shared/string-coerce.js";
import { getChatCommands } from "./commands-registry.data.js";
export function listReservedChatSlashCommandNames(extraNames: string[] = []): Set<string> {
const reserved = new Set<string>();
for (const command of getChatCommands()) {
if (command.nativeName) {
reserved.add(normalizeOptionalLowercaseString(command.nativeName) ?? "");
}
for (const alias of command.textAliases) {
const trimmed = alias.trim();
if (!trimmed.startsWith("/")) {
continue;
}
reserved.add(normalizeLowercaseStringOrEmpty(trimmed.slice(1)));
}
}
for (const name of extraNames) {
const trimmed = normalizeOptionalLowercaseString(name);
if (trimmed) {
reserved.add(trimmed);
}
}
return reserved;
}
function normalizeSkillCommandLookup(value: string): string {
return (normalizeOptionalLowercaseString(value) ?? "").replace(/[\s_]+/g, "-");
}
function findSkillCommand(
skillCommands: SkillCommandSpec[],
rawName: string,
): SkillCommandSpec | undefined {
const trimmed = rawName.trim();
if (!trimmed) {
return undefined;
}
const lowered = normalizeOptionalLowercaseString(trimmed) ?? "";
const normalized = normalizeSkillCommandLookup(trimmed);
return skillCommands.find((entry) => {
if (normalizeOptionalLowercaseString(entry.name) === lowered) {
return true;
}
if (normalizeOptionalLowercaseString(entry.skillName) === lowered) {
return true;
}
return (
normalizeSkillCommandLookup(entry.name) === normalized ||
normalizeSkillCommandLookup(entry.skillName) === normalized
);
});
}
export function resolveSkillCommandInvocation(params: {
commandBodyNormalized: string;
skillCommands: SkillCommandSpec[];
}): { command: SkillCommandSpec; args?: string } | null {
const trimmed = params.commandBodyNormalized.trim();
if (!trimmed.startsWith("/")) {
return null;
}
const match = trimmed.match(/^\/([^\s]+)(?:\s+([\s\S]+))?$/);
if (!match) {
return null;
}
const commandName = normalizeOptionalLowercaseString(match[1]);
if (!commandName) {
return null;
}
if (commandName === "skill") {
const remainder = match[2]?.trim();
if (!remainder) {
return null;
}
const skillMatch = remainder.match(/^([^\s]+)(?:\s+([\s\S]+))?$/);
if (!skillMatch) {
return null;
}
const skillCommand = findSkillCommand(params.skillCommands, skillMatch[1] ?? "");
if (!skillCommand) {
return null;
}
const args = skillMatch[2]?.trim();
return { command: skillCommand, args: args || undefined };
}
const command = params.skillCommands.find(
(entry) => normalizeOptionalLowercaseString(entry.name) === commandName,
);
if (!command) {
return null;
}
const args = match[2]?.trim();
return { command, args: args || undefined };
}