Skip to content

Commit f66c886

Browse files
feat(keyword-detector): show toast notification when ultrawork mode is activated (#393)
* feat(keyword-detector): show toast notification when ultrawork mode is activated When users trigger ultrawork mode (via 'ultrawork' or 'ulw' keywords), a toast notification now appears to confirm the mode is active. The notification is shown once per session to avoid spamming. Changes: - Add detectKeywordsWithType() to identify which keyword type triggered - Show 'Ultrawork Mode Activated' toast with success variant - Track notified sessions to prevent duplicate toasts Closes #392 * fix(keyword-detector): fix index bug in detectKeywordsWithType and add error logging - Fix P1: detectKeywordsWithType now maps before filtering to preserve original KEYWORD_DETECTORS indices. Previously, the index used was from the filtered array, causing incorrect type assignment (e.g., 'search' match would incorrectly return 'ultrawork' type). - Fix P2: Replace silent .catch(() => {}) with proper error logging using the log function for easier debugging when toast notifications fail. --------- Co-authored-by: sisyphus-dev-ai <[email protected]>
1 parent 1c55385 commit f66c886

File tree

3 files changed

+39
-4
lines changed

3 files changed

+39
-4
lines changed

src/hooks/keyword-detector/detector.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import {
44
INLINE_CODE_PATTERN,
55
} from "./constants"
66

7+
export interface DetectedKeyword {
8+
type: "ultrawork" | "search" | "analyze"
9+
message: string
10+
}
11+
712
export function removeCodeBlocks(text: string): string {
813
return text.replace(CODE_BLOCK_PATTERN, "").replace(INLINE_CODE_PATTERN, "")
914
}
@@ -15,6 +20,18 @@ export function detectKeywords(text: string): string[] {
1520
).map(({ message }) => message)
1621
}
1722

23+
export function detectKeywordsWithType(text: string): DetectedKeyword[] {
24+
const textWithoutCode = removeCodeBlocks(text)
25+
const types: Array<"ultrawork" | "search" | "analyze"> = ["ultrawork", "search", "analyze"]
26+
return KEYWORD_DETECTORS.map(({ pattern, message }, index) => ({
27+
matches: pattern.test(textWithoutCode),
28+
type: types[index],
29+
message,
30+
}))
31+
.filter((result) => result.matches)
32+
.map(({ type, message }) => ({ type, message }))
33+
}
34+
1835
export function extractPromptText(
1936
parts: Array<{ type: string; text?: string }>
2037
): string {

src/hooks/keyword-detector/index.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { detectKeywords, extractPromptText } from "./detector"
1+
import type { PluginInput } from "@opencode-ai/plugin"
2+
import { detectKeywords, detectKeywordsWithType, extractPromptText } from "./detector"
23
import { log } from "../../shared"
34
import { injectHookMessage } from "../../features/hook-message-injector"
45

@@ -7,8 +8,9 @@ export * from "./constants"
78
export * from "./types"
89

910
const sessionFirstMessageProcessed = new Set<string>()
11+
const sessionUltraworkNotified = new Set<string>()
1012

11-
export function createKeywordDetectorHook() {
13+
export function createKeywordDetectorHook(ctx: PluginInput) {
1214
return {
1315
"chat.message": async (
1416
input: {
@@ -26,12 +28,28 @@ export function createKeywordDetectorHook() {
2628
sessionFirstMessageProcessed.add(input.sessionID)
2729

2830
const promptText = extractPromptText(output.parts)
29-
const messages = detectKeywords(promptText)
31+
const detectedKeywords = detectKeywordsWithType(promptText)
32+
const messages = detectedKeywords.map((k) => k.message)
3033

3134
if (messages.length === 0) {
3235
return
3336
}
3437

38+
const hasUltrawork = detectedKeywords.some((k) => k.type === "ultrawork")
39+
if (hasUltrawork && !sessionUltraworkNotified.has(input.sessionID)) {
40+
sessionUltraworkNotified.add(input.sessionID)
41+
log(`[keyword-detector] Ultrawork mode activated`, { sessionID: input.sessionID })
42+
43+
ctx.client.tui.showToast({
44+
body: {
45+
title: "Ultrawork Mode Activated",
46+
message: "Maximum precision engaged. All agents at your disposal.",
47+
variant: "success" as const,
48+
duration: 3000,
49+
},
50+
}).catch((err) => log(`[keyword-detector] Failed to show toast`, { error: err, sessionID: input.sessionID }))
51+
}
52+
3553
const context = messages.join("\n")
3654

3755
// First message: transform parts directly (for title generation compatibility)

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
237237
})
238238
: null;
239239
const keywordDetector = isHookEnabled("keyword-detector")
240-
? createKeywordDetectorHook()
240+
? createKeywordDetectorHook(ctx)
241241
: null;
242242
const agentUsageReminder = isHookEnabled("agent-usage-reminder")
243243
? createAgentUsageReminderHook(ctx)

0 commit comments

Comments
 (0)