Skip to content

Commit 391eec9

Browse files
authored
fix(agent): detect bold suggested slash commands (#94)
1 parent f3b4776 commit 391eec9

2 files changed

Lines changed: 60 additions & 13 deletions

File tree

src/agent/command-suggestions.ts

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ export const ACTIONABLE_COMMAND_PREFIXES = [
2424
*/
2525
const PLACEHOLDER_RE = /example\.(?:com|net|org)|<[^>]+>/i;
2626

27+
function cleanCommandCandidate(candidate: string): string {
28+
return candidate
29+
.trim()
30+
.replace(/^[`*_]+/g, "")
31+
.replace(/[`*_]+$/g, "")
32+
.trim();
33+
}
34+
2735
/**
2836
* Scan the assistant's response text for slash commands that match
2937
* actionable prefixes. Returns deduplicated commands in order.
@@ -35,34 +43,40 @@ export function extractSuggestedCommands(text: string): string[] {
3543
const commands: string[] = [];
3644
const seen = new Set<string>();
3745

46+
const addCommand = (candidate: string): void => {
47+
const cmd = cleanCommandCandidate(candidate);
48+
if (!cmd || seen.has(cmd) || PLACEHOLDER_RE.test(cmd)) return;
49+
seen.add(cmd);
50+
commands.push(cmd);
51+
};
52+
3853
// Pattern 1: commands inside backticks — `/plugin enable fetch ...`
3954
// This catches inline code references the LLM wraps in backticks.
4055
const backtickRe =
4156
/`(\/(?:plugin\s+enable|plugin\s+disable|mcp\s+enable|buffer|timeout|set)\s[^`]+)`/gi;
4257
for (const m of text.matchAll(backtickRe)) {
43-
const cmd = m[1].trim();
44-
if (!seen.has(cmd) && !PLACEHOLDER_RE.test(cmd)) {
45-
seen.add(cmd);
46-
commands.push(cmd);
47-
}
58+
addCommand(m[1]);
59+
}
60+
61+
// Pattern 2: commands inside markdown bold — **/mcp enable ...**
62+
// The model often emphasises auth/setup commands this way.
63+
const boldRe =
64+
/\*\*(\/(?:plugin\s+enable|plugin\s+disable|mcp\s+enable|buffer|timeout|set)\s(?:(?!\*\*)[^\n])+)\*\*/gi;
65+
for (const m of text.matchAll(boldRe)) {
66+
addCommand(m[1]);
4867
}
4968

50-
// Pattern 2: bare commands as the start of a line (possibly indented).
69+
// Pattern 3: bare commands as the start of a line (possibly indented).
5170
// Only matched if not already found via backtick pattern.
5271
for (const line of text.split("\n")) {
53-
const trimmed = line.trim();
72+
const trimmed = cleanCommandCandidate(line);
5473
if (
5574
trimmed.startsWith("/") &&
5675
ACTIONABLE_COMMAND_PREFIXES.some((p) =>
5776
trimmed.toLowerCase().startsWith(p.toLowerCase()),
5877
)
5978
) {
60-
// Strip any trailing markdown/punctuation the LLM might append
61-
const cleaned = trimmed.replace(/[`*_]+$/g, "").trim();
62-
if (cleaned && !seen.has(cleaned) && !PLACEHOLDER_RE.test(cleaned)) {
63-
seen.add(cleaned);
64-
commands.push(cleaned);
65-
}
79+
addCommand(trimmed);
6680
}
6781
}
6882

tests/command-suggestions.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,39 @@ describe("extractSuggestedCommands", () => {
3838
expect(extractSuggestedCommands(text)).toEqual(["/set heap 16"]);
3939
});
4040

41+
// ── Markdown-emphasised commands ────────────────────────────
42+
43+
it("should extract a bold /mcp enable command", () => {
44+
const text = [
45+
"The Microsoft Teams MCP server requires authentication.",
46+
"",
47+
"**/mcp enable work-iq-teams**",
48+
"",
49+
"This will prompt you to authenticate in your browser.",
50+
].join("\n");
51+
52+
expect(extractSuggestedCommands(text)).toEqual([
53+
"/mcp enable work-iq-teams",
54+
]);
55+
});
56+
57+
it("should extract an inline bold /mcp enable command", () => {
58+
const text = "Please run **/mcp enable work-iq-teams** to authenticate.";
59+
60+
expect(extractSuggestedCommands(text)).toEqual([
61+
"/mcp enable work-iq-teams",
62+
]);
63+
});
64+
65+
it("should preserve wildcard arguments in bold commands", () => {
66+
const text =
67+
"Run **/plugin enable fetch allowedDomains=[*.bbc.co.uk,feeds.bbci.co.uk]**";
68+
69+
expect(extractSuggestedCommands(text)).toEqual([
70+
"/plugin enable fetch allowedDomains=[*.bbc.co.uk,feeds.bbci.co.uk]",
71+
]);
72+
});
73+
4174
// ── Bare commands on their own line ──────────────────────────
4275

4376
it("should extract a bare /plugin enable on its own line", () => {

0 commit comments

Comments
 (0)