2026-01-01 20:58:02 +09:00
|
|
|
import type { PluginInput } from "@opencode-ai/plugin"
|
2026-01-02 23:03:16 +09:00
|
|
|
import { detectKeywordsWithType, extractPromptText } from "./detector"
|
2025-12-14 11:38:33 +09:00
|
|
|
import { log } from "../../shared"
|
|
|
|
|
import { injectHookMessage } from "../../features/hook-message-injector"
|
|
|
|
|
|
|
|
|
|
export * from "./detector"
|
|
|
|
|
export * from "./constants"
|
|
|
|
|
export * from "./types"
|
|
|
|
|
|
2025-12-19 18:36:14 -08:00
|
|
|
const sessionFirstMessageProcessed = new Set<string>()
|
2026-01-01 20:58:02 +09:00
|
|
|
const sessionUltraworkNotified = new Set<string>()
|
2025-12-19 18:36:14 -08:00
|
|
|
|
2026-01-01 20:58:02 +09:00
|
|
|
export function createKeywordDetectorHook(ctx: PluginInput) {
|
2025-12-14 11:38:33 +09:00
|
|
|
return {
|
|
|
|
|
"chat.message": async (
|
|
|
|
|
input: {
|
|
|
|
|
sessionID: string
|
|
|
|
|
agent?: string
|
|
|
|
|
model?: { providerID: string; modelID: string }
|
|
|
|
|
messageID?: string
|
|
|
|
|
},
|
|
|
|
|
output: {
|
|
|
|
|
message: Record<string, unknown>
|
|
|
|
|
parts: Array<{ type: string; text?: string; [key: string]: unknown }>
|
|
|
|
|
}
|
|
|
|
|
): Promise<void> => {
|
2025-12-19 18:36:14 -08:00
|
|
|
const isFirstMessage = !sessionFirstMessageProcessed.has(input.sessionID)
|
|
|
|
|
sessionFirstMessageProcessed.add(input.sessionID)
|
|
|
|
|
|
2025-12-14 11:38:33 +09:00
|
|
|
const promptText = extractPromptText(output.parts)
|
2026-01-01 20:58:02 +09:00
|
|
|
const detectedKeywords = detectKeywordsWithType(promptText)
|
|
|
|
|
const messages = detectedKeywords.map((k) => k.message)
|
2025-12-14 11:38:33 +09:00
|
|
|
|
|
|
|
|
if (messages.length === 0) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-01 20:58:02 +09:00
|
|
|
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 }))
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-23 14:25:49 +09:00
|
|
|
const context = messages.join("\n")
|
|
|
|
|
|
|
|
|
|
// First message: transform parts directly (for title generation compatibility)
|
|
|
|
|
if (isFirstMessage) {
|
|
|
|
|
log(`Keywords detected on first message, transforming parts directly`, { sessionID: input.sessionID, keywordCount: messages.length })
|
|
|
|
|
const idx = output.parts.findIndex((p) => p.type === "text" && p.text)
|
|
|
|
|
if (idx >= 0) {
|
|
|
|
|
output.parts[idx].text = `${context}\n\n---\n\n${output.parts[idx].text ?? ""}`
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Subsequent messages: inject as separate message
|
2025-12-14 11:38:33 +09:00
|
|
|
log(`Keywords detected: ${messages.length}`, { sessionID: input.sessionID })
|
|
|
|
|
|
|
|
|
|
const message = output.message as {
|
|
|
|
|
agent?: string
|
|
|
|
|
model?: { modelID?: string; providerID?: string }
|
|
|
|
|
path?: { cwd?: string; root?: string }
|
|
|
|
|
tools?: Record<string, boolean>
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-16 21:02:38 +09:00
|
|
|
log(`[keyword-detector] Injecting context for ${messages.length} keywords`, { sessionID: input.sessionID, contextLength: context.length })
|
2025-12-14 11:38:33 +09:00
|
|
|
const success = injectHookMessage(input.sessionID, context, {
|
|
|
|
|
agent: message.agent,
|
|
|
|
|
model: message.model,
|
|
|
|
|
path: message.path,
|
|
|
|
|
tools: message.tools,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (success) {
|
|
|
|
|
log("Keyword context injected", { sessionID: input.sessionID })
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|