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 <sisyphus-dev-ai@users.noreply.github.com>
This commit is contained in:
@@ -4,6 +4,11 @@ import {
|
||||
INLINE_CODE_PATTERN,
|
||||
} from "./constants"
|
||||
|
||||
export interface DetectedKeyword {
|
||||
type: "ultrawork" | "search" | "analyze"
|
||||
message: string
|
||||
}
|
||||
|
||||
export function removeCodeBlocks(text: string): string {
|
||||
return text.replace(CODE_BLOCK_PATTERN, "").replace(INLINE_CODE_PATTERN, "")
|
||||
}
|
||||
@@ -15,6 +20,18 @@ export function detectKeywords(text: string): string[] {
|
||||
).map(({ message }) => message)
|
||||
}
|
||||
|
||||
export function detectKeywordsWithType(text: string): DetectedKeyword[] {
|
||||
const textWithoutCode = removeCodeBlocks(text)
|
||||
const types: Array<"ultrawork" | "search" | "analyze"> = ["ultrawork", "search", "analyze"]
|
||||
return KEYWORD_DETECTORS.map(({ pattern, message }, index) => ({
|
||||
matches: pattern.test(textWithoutCode),
|
||||
type: types[index],
|
||||
message,
|
||||
}))
|
||||
.filter((result) => result.matches)
|
||||
.map(({ type, message }) => ({ type, message }))
|
||||
}
|
||||
|
||||
export function extractPromptText(
|
||||
parts: Array<{ type: string; text?: string }>
|
||||
): string {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { detectKeywords, extractPromptText } from "./detector"
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { detectKeywords, detectKeywordsWithType, extractPromptText } from "./detector"
|
||||
import { log } from "../../shared"
|
||||
import { injectHookMessage } from "../../features/hook-message-injector"
|
||||
|
||||
@@ -7,8 +8,9 @@ export * from "./constants"
|
||||
export * from "./types"
|
||||
|
||||
const sessionFirstMessageProcessed = new Set<string>()
|
||||
const sessionUltraworkNotified = new Set<string>()
|
||||
|
||||
export function createKeywordDetectorHook() {
|
||||
export function createKeywordDetectorHook(ctx: PluginInput) {
|
||||
return {
|
||||
"chat.message": async (
|
||||
input: {
|
||||
@@ -26,12 +28,28 @@ export function createKeywordDetectorHook() {
|
||||
sessionFirstMessageProcessed.add(input.sessionID)
|
||||
|
||||
const promptText = extractPromptText(output.parts)
|
||||
const messages = detectKeywords(promptText)
|
||||
const detectedKeywords = detectKeywordsWithType(promptText)
|
||||
const messages = detectedKeywords.map((k) => k.message)
|
||||
|
||||
if (messages.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const hasUltrawork = detectedKeywords.some((k) => k.type === "ultrawork")
|
||||
if (hasUltrawork && !sessionUltraworkNotified.has(input.sessionID)) {
|
||||
sessionUltraworkNotified.add(input.sessionID)
|
||||
log(`[keyword-detector] Ultrawork mode activated`, { sessionID: input.sessionID })
|
||||
|
||||
ctx.client.tui.showToast({
|
||||
body: {
|
||||
title: "Ultrawork Mode Activated",
|
||||
message: "Maximum precision engaged. All agents at your disposal.",
|
||||
variant: "success" as const,
|
||||
duration: 3000,
|
||||
},
|
||||
}).catch((err) => log(`[keyword-detector] Failed to show toast`, { error: err, sessionID: input.sessionID }))
|
||||
}
|
||||
|
||||
const context = messages.join("\n")
|
||||
|
||||
// First message: transform parts directly (for title generation compatibility)
|
||||
|
||||
@@ -237,7 +237,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
||||
})
|
||||
: null;
|
||||
const keywordDetector = isHookEnabled("keyword-detector")
|
||||
? createKeywordDetectorHook()
|
||||
? createKeywordDetectorHook(ctx)
|
||||
: null;
|
||||
const agentUsageReminder = isHookEnabled("agent-usage-reminder")
|
||||
? createAgentUsageReminderHook(ctx)
|
||||
|
||||
Reference in New Issue
Block a user