diff --git a/assets/oh-my-opencode.schema.json b/assets/oh-my-opencode.schema.json index 8cf28ec99..459ef8135 100644 --- a/assets/oh-my-opencode.schema.json +++ b/assets/oh-my-opencode.schema.json @@ -165,6 +165,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -210,6 +213,9 @@ }, { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string", "enum": [ @@ -297,6 +303,9 @@ }, "providerOptions": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} } }, @@ -338,6 +347,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -383,6 +395,9 @@ }, { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string", "enum": [ @@ -470,6 +485,9 @@ }, "providerOptions": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} } }, @@ -511,6 +529,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -556,6 +577,9 @@ }, { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string", "enum": [ @@ -643,6 +667,9 @@ }, "providerOptions": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} } }, @@ -684,6 +711,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -729,6 +759,9 @@ }, { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string", "enum": [ @@ -816,6 +849,9 @@ }, "providerOptions": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} } }, @@ -857,6 +893,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -902,6 +941,9 @@ }, { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string", "enum": [ @@ -989,6 +1031,9 @@ }, "providerOptions": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} } }, @@ -1030,6 +1075,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -1075,6 +1123,9 @@ }, { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string", "enum": [ @@ -1162,6 +1213,9 @@ }, "providerOptions": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} } }, @@ -1203,6 +1257,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -1248,6 +1305,9 @@ }, { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string", "enum": [ @@ -1335,6 +1395,9 @@ }, "providerOptions": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} } }, @@ -1376,6 +1439,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -1421,6 +1487,9 @@ }, { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string", "enum": [ @@ -1508,6 +1577,9 @@ }, "providerOptions": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} } }, @@ -1549,6 +1621,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -1594,6 +1669,9 @@ }, { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string", "enum": [ @@ -1681,6 +1759,9 @@ }, "providerOptions": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} } }, @@ -1722,6 +1803,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -1767,6 +1851,9 @@ }, { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string", "enum": [ @@ -1854,6 +1941,9 @@ }, "providerOptions": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} } }, @@ -1895,6 +1985,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -1940,6 +2033,9 @@ }, { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string", "enum": [ @@ -2027,6 +2123,9 @@ }, "providerOptions": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} } }, @@ -2068,6 +2167,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -2113,6 +2215,9 @@ }, { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string", "enum": [ @@ -2200,6 +2305,9 @@ }, "providerOptions": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} } }, @@ -2241,6 +2349,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -2286,6 +2397,9 @@ }, { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string", "enum": [ @@ -2373,6 +2487,9 @@ }, "providerOptions": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} } }, @@ -2414,6 +2531,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -2459,6 +2579,9 @@ }, { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "string", "enum": [ @@ -2546,6 +2669,9 @@ }, "providerOptions": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} } }, @@ -2556,6 +2682,9 @@ }, "categories": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "object", "properties": { @@ -2619,6 +2748,9 @@ }, "tools": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -2659,6 +2791,9 @@ }, "plugins_override": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "boolean" } @@ -2932,6 +3067,9 @@ }, "metadata": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": {} }, "allowed-tools": { @@ -2983,6 +3121,9 @@ }, "providerConcurrency": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "number", "minimum": 0 @@ -2990,6 +3131,9 @@ }, "modelConcurrency": { "type": "object", + "propertyNames": { + "type": "string" + }, "additionalProperties": { "type": "number", "minimum": 0 diff --git a/src/hooks/sisyphus-gpt-hephaestus-reminder/hook.ts b/src/hooks/sisyphus-gpt-hephaestus-reminder/hook.ts index a2c1f8150..591d63a02 100644 --- a/src/hooks/sisyphus-gpt-hephaestus-reminder/hook.ts +++ b/src/hooks/sisyphus-gpt-hephaestus-reminder/hook.ts @@ -1,9 +1,32 @@ import type { PluginInput } from "@opencode-ai/plugin" +import { isGptModel } from "../../agents/types" import { getSessionAgent } from "../../features/claude-code-session-state" import { log } from "../../shared" +import { getAgentConfigKey } from "../../shared/agent-display-names" -const TOAST_TITLE = "Use Hephaestus for GPT Models" -const TOAST_MESSAGE = "Sisyphus is using a GPT model. Use Hephaestus and include 'ulw' in your prompt." +const TOAST_TITLE = "NEVER Use Sisyphus with GPT" +const TOAST_MESSAGE = [ + "Sisyphus is NOT designed for GPT models.", + "Sisyphus + GPT performs worse than vanilla Codex.", + "You are literally burning money.", + "Use Hephaestus for GPT models instead.", +].join("\n") + +function showToast(ctx: PluginInput, sessionID: string): void { + ctx.client.tui.showToast({ + body: { + title: TOAST_TITLE, + message: TOAST_MESSAGE, + variant: "error", + duration: 10000, + }, + }).catch((error) => { + log("[sisyphus-gpt-hephaestus-reminder] Failed to show toast", { + sessionID, + error, + }) + }) +} export function createSisyphusGptHephaestusReminderHook(ctx: PluginInput) { return { @@ -12,26 +35,13 @@ export function createSisyphusGptHephaestusReminderHook(ctx: PluginInput) { agent?: string model?: { providerID: string; modelID: string } }): Promise => { - const agentName = (input.agent ?? getSessionAgent(input.sessionID) ?? "").toLowerCase() - const modelID = input.model?.modelID?.toLowerCase() ?? "" + const rawAgent = input.agent ?? getSessionAgent(input.sessionID) ?? "" + const agentKey = getAgentConfigKey(rawAgent) + const modelID = input.model?.modelID - if (agentName !== "sisyphus" || !modelID.includes("gpt")) { - return + if (agentKey === "sisyphus" && modelID && isGptModel(modelID)) { + showToast(ctx, input.sessionID) } - - await ctx.client.tui.showToast({ - body: { - title: TOAST_TITLE, - message: TOAST_MESSAGE, - variant: "error", - duration: 5000, - }, - }).catch((error) => { - log("[sisyphus-gpt-hephaestus-reminder] Failed to show toast", { - sessionID: input.sessionID, - error, - }) - }) }, } } diff --git a/src/hooks/sisyphus-gpt-hephaestus-reminder/index.test.ts b/src/hooks/sisyphus-gpt-hephaestus-reminder/index.test.ts index 09505274f..14b4615b7 100644 --- a/src/hooks/sisyphus-gpt-hephaestus-reminder/index.test.ts +++ b/src/hooks/sisyphus-gpt-hephaestus-reminder/index.test.ts @@ -1,51 +1,53 @@ -import { describe, expect, test, spyOn } from "bun:test" -import { createSisyphusGptHephaestusReminderHook } from "./index" +import { describe, expect, spyOn, test } from "bun:test" import { _resetForTesting, updateSessionAgent } from "../../features/claude-code-session-state" +import { getAgentDisplayName } from "../../shared/agent-display-names" +import { createSisyphusGptHephaestusReminderHook } from "./index" + +const SISYPHUS_DISPLAY = getAgentDisplayName("sisyphus") +const HEPHAESTUS_DISPLAY = getAgentDisplayName("hephaestus") describe("sisyphus-gpt-hephaestus-reminder hook", () => { - test("shows error toast when sisyphus uses gpt model", async () => { - // given - sisyphus agent with gpt model - const showToast = spyOn({ - fn: async () => ({}), - }, "fn") + test("shows toast on every chat.message when sisyphus uses gpt model", async () => { + // given - sisyphus (display name) with gpt model + const showToast = spyOn({ fn: async () => ({}) }, "fn") const hook = createSisyphusGptHephaestusReminderHook({ - client: { - tui: { showToast }, - }, + client: { tui: { showToast } }, } as any) - // when - chat.message runs + // when - chat.message is called repeatedly with display name await hook["chat.message"]?.({ sessionID: "ses_1", - agent: "sisyphus", + agent: SISYPHUS_DISPLAY, + model: { providerID: "openai", modelID: "gpt-5.3-codex" }, + }) + await hook["chat.message"]?.({ + sessionID: "ses_1", + agent: SISYPHUS_DISPLAY, model: { providerID: "openai", modelID: "gpt-5.3-codex" }, }) - // then - error toast is shown - expect(showToast).toHaveBeenCalledTimes(1) + // then - toast is shown for every message + expect(showToast).toHaveBeenCalledTimes(2) expect(showToast.mock.calls[0]?.[0]).toMatchObject({ body: { - title: "Use Hephaestus for GPT Models", + title: "NEVER Use Sisyphus with GPT", + message: expect.stringContaining("burning money"), variant: "error", }, }) }) test("does not show toast for non-gpt model", async () => { - // given - sisyphus agent with non-gpt model - const showToast = spyOn({ - fn: async () => ({}), - }, "fn") + // given - sisyphus with claude model + const showToast = spyOn({ fn: async () => ({}) }, "fn") const hook = createSisyphusGptHephaestusReminderHook({ - client: { - tui: { showToast }, - }, + client: { tui: { showToast } }, } as any) - // when - chat.message runs with claude model + // when - chat.message runs await hook["chat.message"]?.({ sessionID: "ses_2", - agent: "sisyphus", + agent: SISYPHUS_DISPLAY, model: { providerID: "anthropic", modelID: "claude-opus-4-6" }, }) @@ -53,26 +55,40 @@ describe("sisyphus-gpt-hephaestus-reminder hook", () => { expect(showToast).toHaveBeenCalledTimes(0) }) - test("uses session agent fallback when input agent is missing", async () => { - // given - session agent saved as sisyphus - _resetForTesting() - updateSessionAgent("ses_3", "sisyphus") - const showToast = spyOn({ - fn: async () => ({}), - }, "fn") + test("does not show toast for non-sisyphus agent", async () => { + // given - hephaestus with gpt model + const showToast = spyOn({ fn: async () => ({}) }, "fn") const hook = createSisyphusGptHephaestusReminderHook({ - client: { - tui: { showToast }, - }, + client: { tui: { showToast } }, + } as any) + + // when - chat.message runs + await hook["chat.message"]?.({ + sessionID: "ses_3", + agent: HEPHAESTUS_DISPLAY, + model: { providerID: "openai", modelID: "gpt-5.2" }, + }) + + // then - no toast + expect(showToast).toHaveBeenCalledTimes(0) + }) + + test("uses session agent fallback when input agent is missing", async () => { + // given - session agent saved with display name (as OpenCode stores it) + _resetForTesting() + updateSessionAgent("ses_4", SISYPHUS_DISPLAY) + const showToast = spyOn({ fn: async () => ({}) }, "fn") + const hook = createSisyphusGptHephaestusReminderHook({ + client: { tui: { showToast } }, } as any) // when - chat.message runs without input.agent await hook["chat.message"]?.({ - sessionID: "ses_3", + sessionID: "ses_4", model: { providerID: "openai", modelID: "gpt-5.2" }, }) - // then - toast shown via fallback agent lookup + // then - toast shown via session-agent fallback expect(showToast).toHaveBeenCalledTimes(1) }) })