Merge pull request #1770 from code-yeongyu/fix/prometheus-md-only-agent-name-matching

fix: use case-insensitive matching for prometheus agent detection
This commit is contained in:
YeonGyu-Kim
2026-02-12 03:42:21 +09:00
committed by GitHub
5 changed files with 130 additions and 6 deletions

View File

@@ -202,7 +202,7 @@ export async function executeSlashCommand(parsed: ParsedSlashCommand, options?:
if (!command) {
return {
success: false,
error: `Command "/${parsed.command}" not found. Use the slashcommand tool to list available commands.`,
error: parsed.command.includes(":") ? `Marketplace plugin commands like "/${parsed.command}" are not supported. Use .claude/commands/ for custom commands.` : `Command "/${parsed.command}" not found. Use the slashcommand tool to list available commands.`,
}
}

View File

@@ -0,0 +1,5 @@
import { PROMETHEUS_AGENT } from "./constants"
export function isPrometheusAgent(agentName: string | undefined): boolean {
return agentName?.toLowerCase().includes(PROMETHEUS_AGENT) ?? false
}

View File

@@ -1,9 +1,10 @@
import type { PluginInput } from "@opencode-ai/plugin"
import { HOOK_NAME, PROMETHEUS_AGENT, BLOCKED_TOOLS, PLANNING_CONSULT_WARNING, PROMETHEUS_WORKFLOW_REMINDER } from "./constants"
import { HOOK_NAME, BLOCKED_TOOLS, PLANNING_CONSULT_WARNING, PROMETHEUS_WORKFLOW_REMINDER } from "./constants"
import { log } from "../../shared/logger"
import { SYSTEM_DIRECTIVE_PREFIX } from "../../shared/system-directive"
import { getAgentDisplayName } from "../../shared/agent-display-names"
import { getAgentFromSession } from "./agent-resolution"
import { isPrometheusAgent } from "./agent-matcher"
import { isAllowedFile } from "./path-policy"
const TASK_TOOLS = ["task", "call_omo_agent"]
@@ -16,7 +17,7 @@ export function createPrometheusMdOnlyHook(ctx: PluginInput) {
): Promise<void> => {
const agentName = getAgentFromSession(input.sessionID, ctx.directory)
if (agentName !== PROMETHEUS_AGENT) {
if (!isPrometheusAgent(agentName)) {
return
}

View File

@@ -30,11 +30,11 @@ describe("prometheus-md-only", () => {
} as never
}
function setupMessageStorage(sessionID: string, agent: string): void {
function setupMessageStorage(sessionID: string, agent: string | undefined): void {
testMessageDir = join(MESSAGE_STORAGE, sessionID)
mkdirSync(testMessageDir, { recursive: true })
const messageContent = {
agent,
...(agent ? { agent } : {}),
model: { providerID: "test", modelID: "test-model" },
}
writeFileSync(
@@ -55,6 +55,122 @@ describe("prometheus-md-only", () => {
rmSync(TEST_STORAGE_ROOT, { recursive: true, force: true })
})
describe("agent name matching", () => {
test("should enforce md-only restriction for exact prometheus agent name", async () => {
//#given
setupMessageStorage(TEST_SESSION_ID, "prometheus")
const hook = createPrometheusMdOnlyHook(createMockPluginInput())
const input = {
tool: "Write",
sessionID: TEST_SESSION_ID,
callID: "call-1",
}
const output = {
args: { filePath: "/path/to/file.ts" },
}
//#when //#then
await expect(
hook["tool.execute.before"](input, output)
).rejects.toThrow("can only write/edit .md files")
})
test("should enforce md-only restriction for Prometheus display name Plan Builder", async () => {
//#given
setupMessageStorage(TEST_SESSION_ID, "Prometheus (Plan Builder)")
const hook = createPrometheusMdOnlyHook(createMockPluginInput())
const input = {
tool: "Write",
sessionID: TEST_SESSION_ID,
callID: "call-1",
}
const output = {
args: { filePath: "/path/to/file.ts" },
}
//#when //#then
await expect(
hook["tool.execute.before"](input, output)
).rejects.toThrow("can only write/edit .md files")
})
test("should enforce md-only restriction for Prometheus display name Planner", async () => {
//#given
setupMessageStorage(TEST_SESSION_ID, "Prometheus (Planner)")
const hook = createPrometheusMdOnlyHook(createMockPluginInput())
const input = {
tool: "Write",
sessionID: TEST_SESSION_ID,
callID: "call-1",
}
const output = {
args: { filePath: "/path/to/file.ts" },
}
//#when //#then
await expect(
hook["tool.execute.before"](input, output)
).rejects.toThrow("can only write/edit .md files")
})
test("should enforce md-only restriction for uppercase PROMETHEUS", async () => {
//#given
setupMessageStorage(TEST_SESSION_ID, "PROMETHEUS")
const hook = createPrometheusMdOnlyHook(createMockPluginInput())
const input = {
tool: "Write",
sessionID: TEST_SESSION_ID,
callID: "call-1",
}
const output = {
args: { filePath: "/path/to/file.ts" },
}
//#when //#then
await expect(
hook["tool.execute.before"](input, output)
).rejects.toThrow("can only write/edit .md files")
})
test("should not enforce restriction for non-Prometheus agent", async () => {
//#given
setupMessageStorage(TEST_SESSION_ID, "sisyphus")
const hook = createPrometheusMdOnlyHook(createMockPluginInput())
const input = {
tool: "Write",
sessionID: TEST_SESSION_ID,
callID: "call-1",
}
const output = {
args: { filePath: "/path/to/file.ts" },
}
//#when //#then
await expect(
hook["tool.execute.before"](input, output)
).resolves.toBeUndefined()
})
test("should not enforce restriction when agent name is undefined", async () => {
//#given
setupMessageStorage(TEST_SESSION_ID, undefined)
const hook = createPrometheusMdOnlyHook(createMockPluginInput())
const input = {
tool: "Write",
sessionID: TEST_SESSION_ID,
callID: "call-1",
}
const output = {
args: { filePath: "/path/to/file.ts" },
}
//#when //#then
await expect(
hook["tool.execute.before"](input, output)
).resolves.toBeUndefined()
})
})
describe("with Prometheus agent in message storage", () => {
beforeEach(() => {
setupMessageStorage(TEST_SESSION_ID, "prometheus")

View File

@@ -88,7 +88,9 @@ export function createSlashcommandTool(options: SlashcommandToolOptions = {}): T
return `No exact match for "/${commandName}". Did you mean: ${matchList}?\n\n${formatCommandList(allItems)}`
}
return `Command or skill "/${commandName}" not found.\n\n${formatCommandList(allItems)}\n\nTry a different name.`
return commandName.includes(":")
? `Marketplace plugin commands like "/${commandName}" are not supported. Use .claude/commands/ for custom commands.\n\n${formatCommandList(allItems)}`
: `Command or skill "/${commandName}" not found.\n\n${formatCommandList(allItems)}\n\nTry a different name.`
},
})
}