feat(default-mode): auto-activate ultrawork and ralph loop without commands

Add new `default_mode` config section with two boolean fields:
- `ultrawork`: Auto-inject ultrawork mode prompt on main session start
  without requiring the "ultrawork"/"ulw" keyword. Wired through the
  keyword-detector hook — injects once per session, respects existing
  guards (non-OMO agents, planner agents, subagent sessions).
- `ralph_loop`: Auto-start ralph loop on first main session message
  without requiring /ralph-loop or /ulw-loop commands. When ultrawork
  is also enabled, the loop starts in ultrawork mode.

Usage:
  ```jsonc
  {
    "default_mode": {
      "ultrawork": true,    // Always get ultrawork prompt on start
      "ralph_loop": true    // Auto-start ralph loop
    }
  }
  ```

Files: 7 modified/added, ~65 LOC added.
This commit is contained in:
herjarsa
2026-05-19 15:35:21 +02:00
parent f540249838
commit e5463e2db7
7 changed files with 83 additions and 8 deletions
+40 -8
View File
@@ -1,4 +1,5 @@
import type { PluginInput } from "@opencode-ai/plugin"
import type { DefaultModeConfig } from "../../config/schema/default-mode"
import type { KeywordDetectorConfig } from "../../config/schema/keyword-detector"
import {
getMainSessionID,
@@ -16,10 +17,12 @@ import {
removeSystemReminders,
} from "../../shared/system-directive"
import type { RalphLoopHook } from "../ralph-loop"
import { isNonOmoAgent, isPlannerAgent } from "./constants"
import { getUltraworkMessage, isNonOmoAgent, isPlannerAgent } from "./constants"
import type { DetectedKeyword } from "./detector"
import { detectKeywordsWithType, extractPromptText, looksLikeSlashCommand } from "./detector"
const defaultModeUltraworkInjectedSessions = new Set<string>()
function suppressComboStandalones(detected: DetectedKeyword[]): DetectedKeyword[] {
const hasCombo = detected.some((k) => k.type === "hyperplan-ultrawork")
if (!hasCombo) return detected
@@ -31,6 +34,7 @@ export function createKeywordDetectorHook(
_collector?: ContextCollector,
_ralphLoop?: Pick<RalphLoopHook, "startLoop">,
config?: KeywordDetectorConfig,
defaultMode?: DefaultModeConfig,
) {
const disabledKeywords = config?.disabled_keywords
function getRuntimeVariant(input: { variant?: string }, message: Record<string, unknown>): string | undefined {
@@ -74,13 +78,11 @@ export function createKeywordDetectorHook(
const currentAgent = getSessionAgent(input.sessionID) ?? input.agent
// Skip all keyword injection for non-OMO agents (e.g., OpenCode-Builder, Plan)
if (isNonOmoAgent(currentAgent)) {
log(`[keyword-detector] Skipping keyword injection for non-OMO agent`, { sessionID: input.sessionID, agent: currentAgent })
return
}
// Remove system-reminder content to prevent automated system messages from triggering mode keywords
const cleanText = removeSystemReminders(promptText)
const modelID = input.model?.modelID
let detectedKeywords = detectKeywordsWithType(cleanText, currentAgent, modelID, disabledKeywords)
@@ -96,19 +98,49 @@ export function createKeywordDetectorHook(
}
}
if (detectedKeywords.length === 0) {
return
}
const isBackgroundTaskSession = subagentSessions.has(input.sessionID)
if (isBackgroundTaskSession) {
log(`[keyword-detector] Skipping keyword injection for background task session`, { sessionID: input.sessionID })
if (detectedKeywords.length > 0) {
log(`[keyword-detector] Skipping keyword injection for background task session`, { sessionID: input.sessionID })
}
return
}
const mainSessionID = getMainSessionID()
const isNonMainSession = mainSessionID && input.sessionID !== mainSessionID
if (detectedKeywords.length === 0) {
if (defaultMode?.ultrawork && !isNonMainSession && !defaultModeUltraworkInjectedSessions.has(input.sessionID)) {
defaultModeUltraworkInjectedSessions.add(input.sessionID)
const ultraworkMessage = getUltraworkMessage(currentAgent, modelID)
const textPartIndex = output.parts.findIndex(isRealUserTextPart)
if (textPartIndex >= 0) {
const originalText = output.parts[textPartIndex].text ?? ""
output.parts[textPartIndex].text = `${ultraworkMessage}\n\n---\n\n${originalText}`
}
log(`[keyword-detector] Default ultrawork mode auto-activated`, { sessionID: input.sessionID })
ctx.client.tui
.showToast({
body: {
title: "Ultrawork Mode Activated",
message: "Default ultrawork mode enabled. 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,
})
)
}
return
}
if (isNonMainSession) {
detectedKeywords = detectedKeywords.filter(
(k) => k.type === "ultrawork" || k.type === "hyperplan-ultrawork"