fix(skills): deduplicate slash commands by skillName across all interfaces

Move skill-command deduplication by skillName from the Discord-only
`dedupeSkillCommandsForDiscord` into `listSkillCommandsForAgents` so
every interface (TUI, Slack, text) consistently sees a clean command
list without platform-specific workarounds.

When multiple agents share a skill with the same name the old code
emitted `github` + `github_2` and relied on Discord to collapse them.
Now `listSkillCommandsForAgents` returns only the first registration
per skillName, and the Discord-specific wrapper is removed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Shivam
2026-02-26 09:07:14 +05:30
committed by Shakker
parent 5d5fa0dac8
commit 48decefbf4
4 changed files with 70 additions and 49 deletions

View File

@@ -46,6 +46,26 @@ export function listSkillCommandsForWorkspace(params: {
});
}
// Deduplicate skill commands by skillName, keeping the first registration.
// When multiple agents have a skill with the same name (e.g. one with a
// workspace override and one from bundled), the suffix-renamed entries
// (github_2, github_3…) are dropped so every interface sees a clean list.
function dedupeBySkillName(commands: SkillCommandSpec[]): SkillCommandSpec[] {
const seen = new Set<string>();
const out: SkillCommandSpec[] = [];
for (const cmd of commands) {
const key = cmd.skillName.trim().toLowerCase();
if (key && seen.has(key)) {
continue;
}
if (key) {
seen.add(key);
}
out.push(cmd);
}
return out;
}
export function listSkillCommandsForAgents(params: {
cfg: OpenClawConfig;
agentIds?: string[];
@@ -109,9 +129,16 @@ export function listSkillCommandsForAgents(params: {
entries.push(command);
}
}
return entries;
// Dedupe by skillName across workspaces so every interface (Discord, TUI,
// Slack, text) sees a consistent command list without platform-specific
// workarounds.
return dedupeBySkillName(entries);
}
export const __testing = {
dedupeBySkillName,
};
function normalizeSkillCommandLookup(value: string): string {
return value
.trim()