fix(keyword-detector): start ralph-loop when ulw keyword detected (#3113)

Plain 'ulw' keyword now starts the continuation loop via
ralphLoop.startLoop(), matching README promise. Wired through
create-core-hooks.ts and create-transform-hooks.ts.

TDD: 40 keyword-detector tests pass, 1239 hook tests pass, tsc clean.

Closes #3113
This commit is contained in:
YeonGyu-Kim
2026-04-07 15:10:43 +09:00
parent 3e8fd5ff18
commit 717c976c66
4 changed files with 251 additions and 3 deletions
+24 -1
View File
@@ -12,8 +12,20 @@ import {
subagentSessions,
} from "../../features/claude-code-session-state"
import type { ContextCollector } from "../../features/context-injector"
import type { RalphLoopHook } from "../ralph-loop"
import { parseRalphLoopArguments } from "../ralph-loop/command-arguments"
export function createKeywordDetectorHook(ctx: PluginInput, _collector?: ContextCollector) {
const ULTRAWORK_KEYWORD_PATTERN = /\b(ultrawork|ulw)\b/i
function extractUltraworkTask(cleanText: string): string {
return cleanText.replace(ULTRAWORK_KEYWORD_PATTERN, "").trim()
}
export function createKeywordDetectorHook(
ctx: PluginInput,
_collector?: ContextCollector,
ralphLoop?: Pick<RalphLoopHook, "startLoop">
) {
function getRuntimeVariant(input: { variant?: string }, message: Record<string, unknown>): string | undefined {
if (typeof message["variant"] === "string") {
return message["variant"]
@@ -115,6 +127,17 @@ export function createKeywordDetectorHook(ctx: PluginInput, _collector?: Context
sessionID: input.sessionID,
})
)
if (ralphLoop) {
const userTask = extractUltraworkTask(cleanText)
const parsedArguments = parseRalphLoopArguments(userTask)
ralphLoop.startLoop(input.sessionID, parsedArguments.prompt, {
ultrawork: true,
maxIterations: parsedArguments.maxIterations,
completionPromise: parsedArguments.completionPromise,
strategy: parsedArguments.strategy,
})
}
}
const textPartIndex = output.parts.findIndex((p) => p.type === "text" && p.text !== undefined)